写在开头
组件化技术适用于需要多人协作的中大型项目, 如果是一个人的项目且开发人员未实践过组件化方案则不建议采用.
组件化的优点
1. 业务隔离, 使得各业务模块专注于自己的业务实现, 而不必关心其他业务模块.
2. 单独调试, 每个模块可以独立运行, 方便开发调试.
3. 组件可复用性, 针对有重叠业务的不同 App, 可直接使用组件来组装.
4. 适合 AOP.
5. 可以更细粒度的设置组员的代码修改权限.
DRouter: 完美支持多进程的组件化方案
demo 下载
框架特点
* 完美支持多进程, 且不需要使用者去 bindService 或自定义 AIDL.
* 页面路由: 支持给 Activity 定义 url, 然后通过 url 跳转到 Activity, 支持添加拦截器.
* 跨进程的事件总线.
* 支持跨进程的 API 调用.
* 充分实现模块解耦, 页面路由, API 调用, 事件总线均支持跨模块使用.
* 基于 AOP 引导 Module 的初始化以及页面, 拦截器, provider 的自动注册.
如何配置
1. 在 BaseModule 中添加依赖:
2. 在其他需要用到 DRouter 的组件中添加注解处理器的依赖:
3. 多进程配置:
* 如果你的项目需要使用多进程广域路由, 那么请让你的 Application 实现 IMultiProcess 接口, 广域路由默认是关闭状态, 只有实现了该接口才会启用.
* 在 App module 的 build.gradle 文件中, 且必须在 apply plugin: 'com.android.application'之后引用编译插件 RouterPlugin, 具体如下:
- ```
- apply plugin: 'com.android.application'
- apply plugin: "com.dovar.router.plugin" // 必须在 apply plugin: 'com.android.application'之后, 否则找不到 AppExtension
- buildscript {
- repositories {
- google()
- maven {
- url "https://plugins.gradle.org/m2/"
- }
- }
- dependencies {
- classpath "gradle.plugin.RouterPlugin:plugin:1.1.8"
- }
- }
- ```
如何使用
在 Application.onCreate()中完成初始化
DRouter.init(App);
页面路由
动作路由(API 调用)
事件总线
订阅事件
发布事件(在任意线程)
退订事件 (通过 subscribeForever() 订阅时, 需要及时取消订阅)
创建组件初始化入口(非必须)
DRouter 的组件化实现
界面路由
动作路由
事件总线
组件化项目架构图
关于 App 壳工程
用于管理打包配置和设置组件引用.
关于应用组件层
业务中心, 包括业务组件和功能组件(功能组件专指没有 UI 逻辑的业务, 注意区分, 网络请求, 图片加载这些属于基础框架层).
关于公共服务层
管理跨组件调用和公共资源, 详细可参考项目中的 common_service.
为什么要在基础框架层和应用组件层中间多架设一个公共服务层?
* 封装对基础框架层功能 API 的调用, 方便应对日后更换第三方库的需求, 相信很多程序员都经历过更换第三方库 (特别是基础库) 的痛苦啦,
* 如果项目中没有自己封装而是直接引用第三方 API 的话, 等到要换的时候就会发现需要修改的代码实在太多了.
* 储存公用资源和代码, 暴露给上层业务使用, 同时避免这些资源被下沉到基础框架层, 从而减少对基础框架层的非必要更新.
* 在多人协作项目中, 基础框架必须是稳定的, 所以我们希望有尽可能少的 commit 指向基础框架层.
关于基础框架层
与业务无关的通用功能模块, 如网络请求, 图片加载, 通用的自定义控件等.
1. 组件跳转
支持给 Activity 定义 path, 然后通过 path 跳转到 Activity, 可设置跳转拦截器.
2. 进程内组件间通信机制
1. 服务提供者向 DRouter 注册 Action 对其他组件暴露服务.
2. 多对多: LiveEventBus.
3. 多进程通信机制
1. 服务提供者向 DRouter 注册 Action 对其他进程暴露服务, 传递的参数需要实现序列化, 否则会被 DRouter 过滤掉.
2. 多对多: LiveEventBus.
4. 资源文件冲突
1. AndroidManifest.xml 合并:
每个 module 都有一份自己的 AndroidManifest 清单文件, 在 App 的编译过程中最终会将所有 module 的清单文件合并成一份.
我们可以在配置为 Application 的 module 下的 build/intermediates/manifests 路径下找到合成后的 AndroidManifest 文件, 对比编译前后的差异就能大致分析出合并规则和冲突处理规则.
需要注意的是如果在多个 module 中出现同名资源(如 Android:label="@string/app_name"), 且同名资源被合成后的 AndroidManifest.xml 引用, 则会优先取用当前 ApplicationModule 的资源.
2. R 文件:
libModule 中 R 文件里的 id 不再是静态常量, 而是静态变量, 所以不能再使用 switch..case.. 语法操作资源 id
3. 其他 resource:
2. 关于资源的拆分, 一些 style, 常见的 string, 共用的图片, drawable 等资源, 建议存放在 common_service 当中. 对于属于不同模块的资源则应该存放在各自的 module 中.
5. 如何配置 Module 单独调试?
第一步: 在 工程根目录 下的 gradle.properties 下声明对应 module 是否独立运行的属性, 如 isDebugMode. 因为 gradle.properties 中申明的属性在各个 module 的 build.gradle 中可以被直接访问
第二步: 在 module 的 build.gradle 文件中加上红框内的三个部分:
设置 module 类型:
- ```
- if (isDebugMode.toBoolean()) {
- apply plugin: 'com.android.library'
- } else {
- apply plugin: 'com.android.application'
- }
设置 applicationId:
- if (!isDebugMode.toBoolean()) {
- applicationId "com.dovar.router"
- }
使用 sourceSets 配置 AndroidManifest 等
- sourceSets {
- main {
- if (isDebugMode.toBoolean()) {
- manifest.srcFile 'src/debug/AndroidManifest.xml'
- } else {
- manifest.srcFile 'src/release/AndroidManifest.xml'
- }
- }
- }
- ```
更好的实现方式应该是这样的: 设置一个可运行的壳工程, 如示例中的 App. 然后在壳工程中配置组件依赖.
6. 组件化后的 Git 部署
一般我们项目只会对应于一个 Git 仓库, 于是所有开发成员都可以对项目中所有代码进行编辑并提交修改, 但作为项目管理者, 我们想让成员只能提交自己负责的业务代码, 避免成员在开发过程中不小心修改了其他成员的代码导致出现 bug. 而通过组件化后, 我们就可以根据成员开发职责重新设置代码修改权限啦.
假设: 在开发过程中基础框架层不变, 甲负责 module_a, 乙负责 module_b, 丙负责 module_c 和打包, 甲乙丙都可以修改 common_service. 于是我们将 module_a/module_b/module_c/common_service 全都独立成子仓库, module_a 仓库只对甲开放修改权限, module_b 仓库只对乙开放修改权限, module_c 仓库只对丙开放修改权限, 于是就有下图所示的权限分布:
有以下三种 Git 部署方式可供选择:
如何渐进式的进行组件化改造?
完全的组件化拆分并非一两日就能完成, 而我们的项目却总会不断有新的需求等待开发, 版本迭代工作几乎注定了我们不可能将项目需求暂停来做组件化.
那么, 版本迭代与组件化拆分就需要同步进行, 下面是我的建议:
1. 开始准备:
* 新增 App 壳工程, 建议参考本项目中的 App 工程.
* 将你项目当前的 application 工程作为公共服务层(后面直接用 common_service 表示), 当然, 由于还未开始拆分组件, 所以此时它也是最大的业务组件.
* 新增一个组件(后面用 module_search 表示), 建议优先选择一个自己最熟悉或相对简单的业务模块着手, 比如我自己公司项目的搜索模块, 它只有搜索功能且与其他模块交互很少, 所以我选择由它开始.
2. 建立依赖链:
壳工程依赖 common_service.
壳工程依赖 module_search.
module_search 依赖 common_service.(implementation 依赖)
3. 引入 DRouter:
参考上面的 DRouter 使用说明.
4. 分离公共服务层与基础架构层(非必须):
如果之前你的项目没有分离业务层和基础架构层, 那么建议你现在将基础架构从 common_service 中抽离出来.
5. 逐步拆分组件:
拆分第一个组件:
* 前面我们已经新增了 module_search, 所以现在要将搜索模块的代码从 common_service 中抽离并放入搜索组件, 此时搜索组件依然可以直接引用公共服务层的代码, 但公共服务层则只能通过路由使用搜索功能.
* 在开发主线做新需求时, 新增的资源文件建议最好放到对应的组件工程中, 之前的资源文件可以暂时保留在公共服务层, 等待后续由各个组件工程认领走, 尽可能少的积压在公共服务层.
* 权限, Android 四大组件, 特有的第三方库引用等都应该声明在对应的组件 Module 中, 而不应沉入公共服务层, 更不允许进入基础框架层.
拆分第二个组件, 第三个...
(在第一个组件拆分成功并推入市场后, 如果反馈良好, 那么就可以继续拆分其他的组件了)
组件拆分粒度取决于你的业务模块划分和组员开发职责划分, 建议不要拆分出太多的组件.
以上学习方向我们有自己的高清思维方向导图, 架构师有自己讲解的架构视频分享(包括高级 UI, 性能优化, 架构师课程, NDK, 混合式开发: ReactNative+Weex 等多个 Android 技术知识的架构视频资料和各种电子书籍阅读), 视频资料获取方式: 关注 + 点赞 + 加群: 185873940 免费获取!
来源: http://www.jianshu.com/p/29ecc6640b46