在上一篇文章《组件化实践详解(一)》中我们介绍了组件化实践的目标和实践步骤,本文继续说说关于组件化实践遇到的问题及思考。
这条本来我是不想写的,但是很多组件化的文章里都会费尽心思的写组件内的架构设计。
那我也谈一谈我的看法: 首先回归初心,想想组件化的目的,为了各个业务组件可以单独运行 。划重点: 目的是单独运行,把之前在App Module的代码挪到自己单独的Module,然后能够独立运行;而不是大面积重构 !!我也相信对于大部分团队,实际上并没有很多的时间去做重构, 尤其是在做组件化的过程中同时大面积重构,确定做了风险评估吗 ?
对于组件化的整体设计,需要遵循制定的规则,但是对于组件内的架构设计,实际上不需要特殊的要求,代码你爱怎么写就可以怎么写,不管你使用MVC、MVP还是MVVM, 根据各自情况合理选择就好了 。这个话题本来就不属于组件化项目的范畴。
假如之前的几步我们都顺利完成,现在整个Project已经变成了下图整个样子。
那我们的Module要怎么才能跑起来呢?
build.gradle中根据gradle中的一个属性值来判断处于哪种模式下:
if (isDebug.toBoolean()) { apply plugin: 'com.android.application' } else { apply plugin: 'com.android.library' } sourceSets { main { if (isDebug.toBoolean()) { manifest.srcFile 'src/main/debug/AndroidManifest.xml' } else { manifest.srcFile 'src/main/release/AndroidManifest.xml' } } }
在Release中的AndroidManifest配置默认启动的主Activity。
这样一来调试的问题也就解决了。但是实际上这仅仅是一个Demo雏形,我们思考几个带出来的问题:
如何对这个情况做优化: 各个独立Module提供稳定版本的aar !
以下介绍些关于工程化的经验
部分业务组件一定会遇到依赖一些三方组件需要提前初始化的情况,正常我们的做法都是在应用的Application中做的。此时我们在独立的Module开发,没有了应用的Application,那么可以自己创建一个Module的Application, 以下提几种实现的思考 :
Module的Application同时工作于这两种模式下,但是真正打包生成Apk之后实际系统认可的只有App的Application,而别的Module Application只是被系统认为是一个没有特殊意义的普通类。 那我们可以在真正的Application方法调用的时候通过反射调用Module Application的相应方法 ;
Tinker作为热修复的可靠解决方案,想必很多App都会集成,但是Tinker集成稍繁琐的地方就在于: 为了确保Application也能修复,需要改造Application,改造完成之后打的包出来真正的Application已经被修改,而写上了我们逻辑的Application实际上变成了一个普通类,只是相应方法被真正的Application调用 !
把Application放到Library中也不是说移就能移:
面对如此抉择,那到底是移还是不移? 一个好方法是原来Host的Application只做较小改动:并不移出来之前的各种逻辑和组件,而是作为一个普通类,在Library中的Application方法执行时去回调相应Host Application的方法;随后在组件化的过程中逐渐的移出来这些业务和组件。这样的改动成本最小又满足了当下的需要 。
在拆分出来多个Module或者新建Module进行开发,新建资源的时候可能会有命名的冲突,对Gradle熟悉的同学可能会表示使用resourcePrefix来进行限定,但是坦白说 效果一般,倒是不如在编码规范中加上一条以相应Module的标示作为命名的前缀 。
ButterKnife——相信很多同学都用过,这是一个注解框架,一般在绑定View的时候使用,减少了很多无意义的代码。在正常开发中我们用起来也是6的飞起! 然而当ButterKnife跑在Library工程中的时候各种Build失败就出现了:原因在于Android Library中的R文件字段并不是常量,Module在Debug模式下是Application工程可以开心玩耍,等真正集成的时候切换回Release模式就呵呵哒了 。
在ButterKnife8.0之后也支持了在Library中使用,解决方式就是同时生成了一个R2,这个就是常量,因而可以在Library工程中使用。
备注:同时注意R2只能使用在注解中,因而点击事件要写成这样:
@OnClick(R2.id.tv_back_selerole) public void onClick(View view) { if (view.getId() == R.id.tv_back_selerole) { dealBack(); } }
于是我们提出了另外一个名词:去中心化。将基础库进行细粒度的拆分,将开发中一定会用到的例如网络请求、EventBus、公共类等放在了Library中,而将别的不常用三方组件如地图等移出去,只供需要的Module去依赖,而普通的Module则只依赖常用的Library。
历经千辛万苦我们对项目做了组件化实践,那究竟收获了哪些好处呢?
代码结构层次清晰明了;
组件间界限清晰、有明确边界,低耦合;
开发过程体验好,快速编译;
版本周期内没有动到的组件快速回归;
方便A/BTest;