为什么要写这篇文章
组件化已经深得客户端开发大神们的认可,组件化确实进一步优化了客户端团队的开发效率,更适合大团队分组推进,理解组件化的思想,深入研究组件化的实现方案是很必要的。
在接触众多组件化的方案后,自己也萌生了想搭建一套集众家之所长的组件化方案。
而从【得到】组件化方案入手,主要是【得到】组件化开源的demo比较完整,日期也比较新。
这篇文章的主要目的是想研究一下【得到】组件化比其他组件化方案更优的点,就像得到官方文章中说的一样:
在设计之初参考了目前已有的组件化和插件化方案,站在巨人的肩膀上又加了一点自己的想法
综合各家之所长,再加上自己的一点想法,整理出自己习惯的一套组件化方案
得到App组件化的大概介绍
开源的组件化方案并不少,放出来的组件化文章就更多了,但是像【得到】技术团队这样连同方案和解析文章全部一起打包,毫无保留的开源给开发者的确实是少数。
只是官方文章难免站在了主人的角度来诠释框架的整体,对于一个刚刚接触这套框架的学习者来说还是有点难懂,所以我这里再次对这个方案的实现从一个学习者的角度梳理一下,对其他学习者应该也是有意义的。
【得到】组件化优缺点都比较明显
优点:代码隔离方案很实用,并且逻辑简单清晰好理解
缺点:暂时还没有加入 Activity注解-apt自动生成-ui路由 ,发布aar仍需改动isRunAlone的值。
【得到】组件化官方的解析文章中已经有放出项目的架构图,因为省略了一些模块,所以对于初学者来说看得不算是非常清晰,我这里有做了一版比较全的。
从图中我们可以看得出来
componentservice基本就是base moudle
业务组件与主host之间靠gradle.properties确定依赖关系,但这里只是依赖关系的标记而已,并没有直接依赖上,也就是开发期组件之间是不能直接调用的,否则编译会报错,从根本上杜绝了直接调用的问题。
所有业务组件统一进入build-gradle处理,在编译期自动添加依赖关系,也就是完成了【代码隔离】
【得到】组件化的代码隔离,主要靠两个步骤
UI路由方案的解读其实已经非常多,github上的明星项目就好几个呢,所以我就不过多解读了。
这里我们着重了解一下【得到】组件化有关于代码隔离的实现方案。
本文很少贴大段的源码,主要是想用尽可能简明的语音和清晰的图来说明实现方法。所以最好是熟读官网文章记录下自己的问题之后,一边对照我这篇解析文章,一边对照着demo源码来理解才是最佳途径。
主要靠两个步骤
build-gradle插件
componentservice服务接口
build-gradle的作用
从图中可以看出包括主项目App在内的每一个moudle都通过在build.gradle文件中
apply plugin: 'com.dd.comgradle'
引入自定义插件build-gradle
而apply plugin 实际上是调用的gradle的project中的方法
void apply(Mapoptions)
如果你疑问为什么调用方法没有括号,没有参数,请先了解一下Gradle DSl的基础知识和语法特性。深入理解Android之Gradle
每个moudle都调用了apply方法后,实际上都执行到了ComBuild.groovy中的
void apply(Project project) {}
至此就算明白了 每个moudle在编译期都会执行到ComBuild.groovy的apply方法中。
apply方法中做了这些事请:
调用getTaskInfo()方法,读取当前Task信息,标记是否是组装(assemble)Task,是否是调试(DEBUG)模式
如果是Assemble(组装类)的Task,调用fetchMainmodulename()方法,确定当前编译的moudle,从rootProject中读取mainmodulename的初始值
从gradle.properties中取出isRunAlone的值,根据isRunAlone执行不同的操作
如isRunAlone=true,则代表当前的moudle就是主项目,引入plugin: ‘com.android.application’插件
如果当前moudle是否是主moudle,且当前task是否为Assemble(组装类)Task,则根据gradle.properties中的debugComponent和compileComponent分情况自动添加依赖文件,组件发布的aar就是这里使用的。
根据每个moudle的build.gradle文件中isRegisterCompoAuto字段判断是否自动注册moudle所提供的服务,这里涉及到何时调用每个moudle中的IApplicationLike,什么方式调用?
调用的方式有两种:
A. 编译期字节码插入的方式,就是扫描所有的ApplicationLike类后,自动写入调用代码,具体查看ComCodeTransform.groovy中的代码逻辑
B. 反射调用,类似下面的代码
Router.registerComponent("com.mrzhang.reader.applike.ReaderAppLike")
如isRunAlone=false,则代表当前的moudle是library,引入plugin: ‘com.android.library’插件,然后看是否是assembleRelease Task:
如果是assembleRelease,则发布组件aar
如果不是assembleRelease Task,则把当前moudle作为library随同主项目一起编译而已
componentservice服务接口的实现
相比build-gradle插件,componentservice服务接口的实现就非常简单了,官网博客中说是面向接口编程还是非常恰当的,如下图所示:
从图中可以看出,componentservice中的Router存放的所有组件所提供的服务,而Router本身只是一个map而已,原理非常简单
组件B的开发人员再编写好组件B之后,需要提供接口文档
组件A的开发人员想用组件B的服务时,就需要根据接口文档有关于组件B的服务描述中,找到组件B所提供的服务的名字,然后调用router.getService(服务全路径名);即可获取响应的服务
而面向接口编程其实说的是每个应用到服务的地方都是针对父类编程,以达到接口和实现的分离目的
由于在开发期是面对存放在componentservice目录下的服务父类编程,而在提供服务的组件中的IApplicationLike中注册了组件中关于服务的具体实现。
上面的文字描述可能还是不太明白,所以还是用图表示好了
总结
这是我对于【得到】组件化方案中的代码隔离部分的一个梳理,可能有些地方描述不清楚或者有理解错误,欢迎大家指正。
如有问题可以一起讨论,个人qq 278960878.
希望对大家有帮助!