从2017年只有几个大厂在做组件化,到今天已经繁花似锦。
越来越多的团队,越来越多的项目都做了组件化。
大叔相信即使你没有做过组件化项目,但是,对组件化也早就听烂了。
但是,组件化开发多少有些技术门槛。
有很多大神写过相关文章,通俗易懂的不多。深入浅出的更不多。
不才,愿意冒着不要脸的风险一试,通俗易懂、深深浅浅的来聊聊组件化开发,如果对你有一点点启发,请记得回来给大叔点个赞。
这种分层架构,有什么用呢?
分解成多module的项目结构,就是组件化开发了吗?
当然不是,多module分层的项目结构,只是组件化开发的一部分。只是组件化开发的基础。
大叔,搜索了很多资料,发现,对于组件化开发,并没有很严格的定义。
当然,我们没有必要,过于纠结 ”组件化开发的定义“;
我们更关注这种开发思想对项目带来的好处以及在团队中如何运用。
下面是我的理解,如有出入,欢迎提出来一起讨论。
尤其在大团队,大项目上,组件化的优势会更加凸显。
大项目分解成一个个小型项目,相当于将一个复杂的问题拆解成一个个相对简单的问题。
每个成员,可以专注在自己相关业务的module上。
同层的代码不能相互调用。底层的代码也不能调用上层。
这种编译隔离,带来了,模块间的高度解耦。
让模块的依赖关系清晰。
(如果构建正确)组件化设计的系统,比传统的整体设计具有更高的可重用性。
什么是组件?什么是模块?
组件强调复用,模块强调职责划分。 他们没有非常严格的划分。
达到可复用要求的模块,那么这个模块就是组件。
ba层的module必须是可复用的,如果项目设计的好,business层都能被复用,每个module都能成为组件。
可重用性是组件化思想的核心。
如此架构,是否也适合技术中台的架构?
如果我们要为某个已经存在的组件,重新开发一个新组件,将变得非常可行。
组件内的重构也将变得非常可行。
新的组件的设计只要保证对外提供的接口,完全符合,旧组件对外提供的接口
我们想象下,在app运行时,business中的组件可以动态加载,也可动态卸载。
那么我们可以轻松实现组件的懒加载:用户用到的组件,那么就加载进来。用完之后便可以卸载。
大的android工程项目,build一次要到5分钟左右,太浪费时间了。
拆成多个组件之后,如果每个组件都能单独build,单独测试,那么将大大提升开发效率。
上面讨论的这些优势,并不是将简单将 单工程 拆分成 分层的多module工程结构 就能获得这些优势。
想要获得这些优势,还任重道远,我们还需要解决很多问题,才能让我们的项目具备上面的说的优势。
通过arouter通信。
arouter是阿里开源的一个项目。github.com/alibaba/aro…
通过arouter跨module跳转activity
@route(path = "/test/activity")//申明路由public class youractivity extend activity { ...}//通过路由启动activityarouter.getinstance().build("/test/activity").withlong("key1", 666l).navigation();复制代码
通过arouter在module间共享对象,实现module间通信。
比如:我们有一个账号模块 business:account ,提供了登录、登出、用户信息查询等业务。
同级的其他模块,如何跟账号模块通信?获取用户的登录状态以及用户相关信息?
public class accountbean { private string name; private int age; //....}public interface iaccountrvice extends iprovider { void login(context context);//登录 void logout(context context);//登出 accountbean getaccountbean();//获取账号信息}复制代码
对外的数据结构和接口定义。
@route(path = businessroutepath.moduleaccount.account)public class accountrviceimpl implements iaccountrvice { //.....}复制代码
bussiness:account模块中的实现。
ia女人无需楚楚可怜ccountrvice accountrvice = arouter.getinstance().navigation(iaccountrvice.class);accountrvice.login(activity);accountbean bean = accountrvice.getaccountbean(); 复制代码
但是问题来了:
同层的其他模块,如何,能拿到arouter的path?
同层的其他模块编译时,如何,共享accountbean类、iaccountrvice接口?
这就是模块之间的编译隔离,带来的问题。
我们很自然的想到了framework模块,或者ba层的其他模块。
我们只要将这些path定义、accountbean类、iaccountrvice接口,下沉到ba层,就可以实现编译上的代码共享。
如此一来,就带来了,另一个问题:代码的中心化问题。
简单的path字符串定义,放在framework倒是还好。
如果所有business模块对外提供的接口和数据结构,都定义到framework的话,问题就有点严峻。
将会破坏:组件的 可替代性、可重用性、组件间耦合度
因为framework是基础模块嘛,所有business模块都依赖的模块,如此,不管你的business1模块是否依赖business2模块的对外接口,都会存在这一层依赖。
模块间的代码边界出现一些劣化。缺少了编译上的隔离。许多模块将会变得不够“独立”了。
可替代性、可重用性 越来越弱,想要替换或者复用某个business组件将变得越来越难。
将会导致,我们很难知道,哪些business对哪些business 接口有依赖。
同时,framework模块随着功能迭代,会不断膨胀。
这就是,中心化的问题。
于是我们很自然的想到了一个解决方案:
实现了另一种接口暴露的形式——“.api化”。
将 business模块 对外提供的接口单独抽到 business-api 模块中。其他依赖他的模块只需要依赖他的business-api即可。
这个方案如何实践下去呢?
微信团队出了一个很巧妙的方案,这个方案对android的组件化开发,产生了非常深远的影响。
后面很多做组件化开发的团队,在解决中心化问题基本都会用到类似的方案。
以下为,微信官方博客的原文引用:
使用方式和思路都很简单。对于java文件,将工程里想要暴露出去的接口类后缀名从“.java”改成“.api”,就可以了。
而且并不只是java文件,其他文件如果也想暴露,在文件名后增加”.api”,也一样可以。
当然,要让工程支持这样的方式,gradle文件肯定会有一点改变。
它的实现原理也相当简单:自动生成一个“sdk”工程,拷贝.api后缀文件到工程中就行了,后面其他工程依赖编译的只是这个生成的工程。简单好用。
api方案有点类似于android的aidl的思路。
gradle 根据src/api文件来,自动生成{modulename}-api模块。如果,src/api文件不存在,将不会自动生成 {modulename}-api 模块。复制代码
通过api模块来解决代码中心化问题带来的好处:
让各个business的之间的依赖明确让business对外提供的接口明确。从而加强了模块的:可替代性。
只要两个business对外提供的api一致,就可以相互替换。
模块变多了,项目变大了,整个项目的编译速度变慢了。
业内有两种常用做法。
方案一:动态配置 build.gradle。只要让单个的组建能编译成app就拈轻怕重的意思能单独测试。方案二:多壳app方案来自,在聚美优品。这里需要注意:假如,demo1是business1的壳app。那么demo1还需要依赖哪些businessxxx呢?刚好,前面做的api化,能排上用场。business1依赖的businessxxx-api模块对应的businessxxx模块,demo1也需要依赖。为甚?因为,business1依赖的businessxxx-api模块,意味着,business1需要依赖 businessxxx提供的功能,比如要跳转到businessxxx的activity?或者,要获取businessxxx的对象。如何防止资源名冲突?
比如businessa 和 businessb都在drawable目录下,都有一张同名的图片。
这两张图片只有一张会被打包到apk中,被使用。
这样就容易出现混乱。
这个问题比较好解决。让每个模块的资源名固定一个前缀。只要模块之间的前缀不一样就不会冲突。
gradle的resourceprefix配置,刚好符合我们的需求。
如下配置,如果module中存在资源不以app_开头,lint走查会报warnning。注意不会编译失败。
android { resourceprefix 'app_'}复制代码
如下截图的warning:
可以通过booster的资源内联工具解决,r类的冗余。
详细可以自己查看booster官网,booster是didi开源的一个插件。
零基础学习英语booster.j中秋节是哪天ohnsonlee.io/feature/shr…
没有找到很好的解决方案。
每个方案都有缺陷
比如说,business1和business2都要用到同一张图片。
那么这张图片该放到哪里呢?
1、把他放到api模块里来共享资源这种,并非功能依赖,放到api模块也不太合适。因为这样可能造成business1和business2模块原本没有关联也没有依赖;但因为共用同一个资源,却导致存在了依赖。2、在business1和business2中都放一个图片如此会增大包体3、在business1和business2中都放文件名同名的图片,让编译时资源合并的时候只使用同一张图片。如此一来,放开各个模块资源命名,也容易导致开发时发生冲突。自定义lint规则,让存在common和{modulename}两种前缀?4、将这张图片下沉到ba层,如:framework模块,或者,单开一个lib-resource如此一来,将会出现资源中心化问题。上面的方法多少都有些缺陷,大叔还没有找到一个优雅的方式。如果你有什么好想法,一定要留言告诉大叔,大叔在此谢过你了。
千万不能这么操作。
假如:在 business/tting 中直接在gradle配置中依赖,business:account。
那么编译上的代码隔离就彻底被毁。就跟不要谈组件的可重用性、可替代性了。
implementation project(“:business:account”) 复制代码
各个组件如何获得application.attach()、application.oncreate()、application.onterminate()等的回调。
未完待续
未完待续,待踩过坑,实现了,再来写。
未完待续,待踩过坑,实现了,再来写。
待继续探索
最后我们再回到,组件化本身上来。
组件化开发不仅仅是一种多module分层的项目结构;他不仅仅是一种架构;他更是一种架构思想,一种追求模块复用精神。
有人说小项目没有必要做组件化开发。大叔不这么认为,小项目依然适合做组件化,除非你们团队只有一个项目,并且项目几乎不需要迭代。组件跨项目的复用也是一件让人十分兴奋的优势。
前几年听烂了的技术中台,跟组件化的架构也不谋而合。
本文发布于:2023-04-05 15:27:35,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/207f2aeb0a62bee7895ecd32fa09b3bc.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:android组件化架构(速览android组件和使用技巧).doc
本文 PDF 下载地址:android组件化架构(速览android组件和使用技巧).pdf
留言与评论(共有 0 条评论) |