Android 组件化思路

1. 引言

  • 几十万行甚至上百万行代码的大工程,100%代码编译,打包时间长;
  • 业务交叉、耦合严重、修改一个问题可能引出多个问题;
  • 非本次迭代业务代码被修改上线;
  • 代码零乱影响开发心情和效率;
  • 并行开发难,上线测试范围广;

如果我们能把100%的代码中,70%的代码稳定下来,只有30%的业务代码经常改动,是否就可以降低维护成本,提高开发效率和稳定性;
70%的代码中,形成各个独立的功能模块或组件沉淀稳定下来,就可极大的减小对整体代码开发和修改频率;
30%的代码中,负责主要业务代码开发和产品发布,再划分为不同业务小组维护,提高了开发效率,也降低了维护成本;

所以可以形成一个开发思路:稳定(大)中台,灵活(小)前台

2. 目标

  • 去掉任何一个模块,主工程可以运行;

  • 任意一个子模块,可以独立运行;

  • 自动化,持续集成;

  • APP

    • 持续集成,服务产品快速迭代,同时保持高质量
    • 降低代码耦合度,提高编译速度和开发效率,避免修改一处编译整个工程;
    • 按需打包,根据依赖业务打包;
    • 缩小测试范围,减小测试成本,降低代码出错风险,提高App稳定性
    • 快速响应,快速迭代发版和热修复;
  • 组件

    • 独立编译和发布,组件回滚有迹可循,便于持续集成;
    • 高内聚低耦合,提高组件复用性;
  • 团队

    • 方便团队协作;
    • 开发职责明确;

3. 问题

  1. 组件划分原则和粒度?
    • 组件代码分布思考;
    • 组件代码修改频率和稳定性思考;
    • 组件化改造后开发效率的平衡
    • 对现存代码大工程的拆分思路;
  2. 组件间通信方式?
    • Router 维护方式;
    • Event;
  3. 组件的管理方式?
    • 远程or本地?
  4. 代码重构和文件查找?

4. 组件划分原则

大原则:去中心化,平级组件无依赖;业务组件粗粒度;功能组件细粒度;基础组件高稳定性;

组件化工程结构

app  -  壳工程
app-support - App支持组件

module-x (A、B、C) - 业务组件

library-x (a,b,c,d,e,f) - 功能组件

library - 基础组件

4.1 App壳工程

  • 命名规则:app
  • 目标:壳工程,负责打包;去掉任何一个业务组件,不影响编译打包运行
  • 依赖关系:只依赖业务组件;

4.2 业务组件划分规则

  • 命名规则:module- + 后缀名
  • 划分规则:根据公司或部门实际业务划分,粗粒度,边界清晰,便于团队管理,便于组件划分,业务组件不要太多;
  • 修改频率:;业务组件作为修改频次最高的组件,减少业务组件,可以提高开发效率;相反如果有拆的过于零散,会造成开发一块业务,要横跨好几个组件才能完成,如果再加上组件独立git仓库,必然影响开发效率,这也是我们在之前拆分过程吸取的教训。
  • 依赖关系:业务组件之间不能存在依赖,只能向下依赖功能组件或Base组件

4.3 功能组件划分规则

  • 命名规则:library- + 后缀名
  • 划分规则:细粒度高内聚,为业务组件提供支撑,可以是基础服务,也可以是封装了单一功能组件;
  • 修改频率:;一旦功能组件稳定,修改频率会大幅降低,向上提供稳定的服务或功能;
  • 依赖关系:功能组件之间不能依赖,只能向下依赖Base组件;
  • 主要作用:1、提供基础服务或独立功能;2、抽取封装业务组件内的通用或独立功能,为业务组件减负;3、作为APP的中台代码,形成沉淀,提高稳定性;

4.4 Base组件规则

  • 命名规则:library,不用加后缀,如果在一个AS工程内,library可以友好的显示到上面;如果是独立git远程aar,groupId本身也可以区分;
  • 划分规则:进入Base代码要斟酌考虑,区分代码是该封装到功能组件,还是下沉到Base,判断标准是代码通用性,这部分代码是所有组件都需要依赖,还是仅仅只给某个业务组件依赖;
  • 修改频率:;下沉到Base里的代码,一定要谨慎考虑,高通用性、高稳定性;
  • 主要作用:为上层组件提供基础支撑;提供App开发常用基础功能;完全非业务耦合;封装三方调用;基础工具类调用;一个APP所不可少的代码封装;

4.5 app-support,特殊代码存放规则思考

总有一部分代码不好安放,也是最纠结的地方,比如:启屏页,引导页,首页框架、搜索、我的、设置、关于等等,这部分代码,不属于某块业务,放到功能组件也不合适,更不能放到Base,而且每个App都需要这些代码才能运行,这部分代码一种可行的方式是,创建一个 app-support独立存放。这个组件和业务组件的区别是,app需要app-support

  • 命名规则: app-support
  • 划分规则:为App运行提供支撑;不明显属于某块独立业务;
  • 修改频率:
  • 依赖关系:只依赖到APP内。后面描述废弃:可以依赖业务组件,做内容填充;比如MainActivity内依赖的Fragment,Fragment本身可以属于某块业务,比如关注列表,如果你有UGC业务,明显属于UGC业务组件 ;

5. 组件间通信方式

Activity 界面之间调用

* 组件对外开放的Activity界面,全部通过定义的Router协议进行跳转;
* 拦截器:针对需要登录,全局Token失效踢出等进行拦截跳转;
* 降级支持:页面未找到,提供Toast、空页面,H5页面等降级说明;
* Router协议的存放:不建议提供中心存放,避免中心依赖;业务组件里有很多对外调用,不要直接把调用写在onClick方法内,在module内部提供一个控制类,统一调用控制即可。
* https://github.com/chenenyu/Router,轻量、灵活、易懂易用;
* https://github.com/alibaba/ARouter,功能更丰富强大;

事件或消息跨组件传递

* 基本原则是,全局事件要慎重考虑,过多的全局事件,必然需要中心存放和频繁修改底层组件,带来不稳定性和耦合;
* 如果有中心事件源在中台,需要接收和抛出,那么设计好数据结构和接口,使用观察者模式,业务调用层扩展和处理;事件源只传递基础数据,不负责业务处理;

6. 大工程的拆分思路

* 分析业务和工程代码,形成组件化架构图,提供整体拆分依据;
* 目前已独立的服务,按照组件原则拆分解耦,提供远程aar调用;
* 被依赖基础组件,那些组件是不拆分,阻塞其他代码拆分,优先拆分;
* 底层Base组件,优先进行;
* 拆功能组件,留业务组件,业务组件后划分;
* 自底向上拆分比较容易点;
* 不要一下子变成远程,先再AS内独立module,再变成远程仓库;
* 各module自己管理相关业务数据,不要所有http请求都放到一个类;

拆分技巧篇:Andriod 组件化实用技巧