Android组件化,全面掌握(节选)
本文转载自 “终于懂了” 系列:Android组件化,全面掌握!。版权归原作者所有,此处仅作个人学习备份。
注:因原文篇幅较长,此处为前半部分节选(背景、组件化理解、组件独立调试),完整内容(含 ARouter 路由跳转、组件间通信、Application 生命周期分发等)请参阅原文。
一、背景
随着项目逐渐扩展,业务功能越来越多,代码量越来越多,开发人员数量也越来越多。此过程中,你是否有过以下烦恼?
- 项目模块多且复杂,编译一次要5分钟甚至10分钟?太慢不能忍?
- 改了一行代码 或只调了一点UI,就要run整个项目,再忍受一次10分钟?
- 合代码经常发生冲突?很烦?
- 被人偷偷改了自己模块的代码?很不爽?
- 做一个需求,发现还要去改动很多别人模块的代码?
- 别的模块已实现的类似功能,自己要用只能去复制一份代码再改改?
- “这个不是我负责的,我不管”,代码责任范围不明确?
- 只做了一个模块的功能,但改动点很多,所以要完整回归测试?
- 做了个需求,但不知不觉导致其他模块出现bug?
如果有这些烦恼,说明你的项目需要进行 组件化 了。
二、组件化的理解
2.1 模块化
在介绍组件化之前,先说说模块化。在 Android Studio 中,新建工程默认有一个 App module,还可以通过 File->New->New Module 新建 module。这里的 “module” 实际和我们说的 “模块” 基本是一个概念。也就是说,原本一个 App 模块承载了所有的功能,而模块化就是拆分成多个模块放在不同的 Module 里面,每个功能的代码都在自己所属的 module 中添加。
以京东为例,大致可以分为 “首页”、“分类”、“发现”、“购物车”、“我的”、“商品详情” 六个模块。通常还会有一个通用基础模块 module_common,提供 BaseActivity/BaseFragment、图片加载、网络请求等基础能力,然后每个业务模块都会依赖这个基础模块。
那么业务模块之间有没有依赖呢?很显然是有的。例如 “首页”、“分类”、“发现”、“购物车”、“我的”,都是需要跳转到 “商品详情” 的,必然是依赖 “商品详情”;而 “商品详情” 是需要能添加到 “购物车” 能力的。所以这些模块之间存在复杂的依赖关系,即模块间有着高耦合度。高耦合度加上代码量大,就极易出现上面提到的那些问题。
为了解决模块间的高耦合度问题,就要进行组件化了。
2.2 组件化介绍 — 优势及架构
组件化,去除模块间的耦合,使得每个业务模块可以独立当做 App 存在,对于其他模块没有直接的依赖关系。 此时业务模块就成为了业务组件。
除了业务组件,还有抽离出来的业务基础组件(提供给业务组件使用但不是独立的业务,例如分享组件、广告组件);还有基础组件(单独的基础功能,与业务无关,例如图片加载、网络请求等)。
组件化带来的好处:
- 加快编译速度:每个业务功能都是一个单独的工程,可独立编译运行,拆分后代码量较少,编译自然变快。
- 提高协作效率:解耦使得组件之间彼此互不打扰,组件内部代码相关性极高。团队中每个人有自己的责任组件;降低团队成员熟悉项目的成本;对测试来说,只需重点测试改动的组件,而不是全盘回归测试。
- 功能重用:组件类似我们引用的第三方库,只需维护好每个组件,一键引用集成即可。
期望的组件化架构:
- 组件依赖关系是上层依赖下层,修改频率是上层高于下层。
- 基础组件是通用基础能力,修改频率极低,作为 SDK 可供公司所有项目集成使用。
- common 组件,作为支撑业务组件、业务基础组件的基础(BaseActivity/BaseFragment 等),同时依赖所有的基础组件,并统一了基础组件的版本号。
- 业务组件、业务基础组件 都依赖 common 组件。但业务组件之间不存在依赖关系。
- 最上层是主工程,即所谓的 “壳工程”,主要是集成所有的业务组件、提供 Application 唯一实现、gradle、manifest 配置,整合成完备的 App。
2.3 组件化开发的问题点
核心问题是 业务组件去耦合。针对业务组件有以下问题:
- 业务组件,如何实现单独运行调试?
- 业务组件间没有依赖,如何实现页面的跳转?
- 业务组件间没有依赖,如何实现组件间通信/方法调用?
- 业务组件间没有依赖,如何获取 fragment 实例?
- 业务组件不能反向依赖壳工程,如何获取 Application 实例、如何获取 Application onCreate() 回调(用于任务初始化)?
三、组件独立调试
每个 业务组件 都是一个完整的整体,可以当做独立的 App,需要满足单独运行及调试的要求。有两种方案:单工程方案(组件以 module 形式存在,动态配置组件的工程类型)、多工程方案(业务组件以 library module 形式存在于独立的工程)。
3.1 单工程方案:动态配置组件工程类型
Android Gradle 提供了两种插件:Application 插件(com.android.application,输出 APK)、Library 插件(com.android.library,输出 AAR)。App module 配置的是 Application 插件,业务组件 module 配置的是 Library 插件。想要实现业务组件的独立调试,需要把配置改为 Application 插件;独立开发调试完成后,又需要变回 Library 插件进行集成调试。
用 AndroidStudio 创建项目后,会在根目录生成一个 gradle.properties 文件,在这个文件定义的常量可以被任何一个 build.gradle 读取。所以可以在 gradle.properties 中定义一个常量 isModule,true 为独立调试,false 为集成调试:
1
2
3
//gradle.properties
#组件独立调试开关, 每次更改值后要同步工程
isModule = false
1
2
3
4
5
6
7
//build.gradle
//注意gradle.properties中的数据类型都是String类型,使用其他数据类型需要自行转换
if (isModule.toBoolean()){
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
3.1.2 动态配置 ApplicationId 和 AndroidManifest
组件在独立调试时是一个 App,需要 ApplicationId 和启动页;集成调试时则不需要。所以 ApplicationId、AndroidManifest 也需要用 isModule 来配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//build.gradle (module_cart)
android {
defaultConfig {
if (isModule.toBoolean()) {
// 独立调试时添加 applicationId ,集成调试时移除
applicationId "com.hfy.componentlearning.cart"
}
}
sourceSets {
main {
// 独立调试与集成调试时使用不同的 AndroidManifest.xml 文件
if (isModule.toBoolean()) {
manifest.srcFile 'src/main/moduleManifest/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
其中独立调试用的 AndroidManifest 新建于 moduleManifest 目录,指定了 Application、启动 activity;而原本自动生成的 manifest 不指定 Application 与启动 activity。
3.2 多工程方案
多工程方案中,业务组件以 library module 形式存在于独立的工程,独立工程自然就可以独立调试了。当所有业务组件都拆分成独立组件时,原本的工程就变成一个只有 app 模块的壳工程,用来集成所有业务组件。
集成调试时使用 maven 引用组件:把组件的 AAR 包发布到公司的 maven 仓库,然后在壳工程中使用 implementation 依赖即可,和使用第三方库一样。AAR 包分为快照版本(SNAPSHOT,开发阶段调试使用)和正式版本(Release,正式发版使用),通过在 module 中新建 maven_push.gradle 并配置 uploadArchives 任务发布。
(后续内容) 原文还包含:ARouter 实现组件间页面跳转、组件间通信(暴露服务/接口下沉)、组件间获取 Fragment、Application 生命周期在各组件的分发等。完整内容请查阅原文。