愿你成为自己的太阳,无需借谁的光
下文列出组件化的思路和步骤,按照如下思路和步骤,可以加快组件化工作的速度
1 诊断分析
1.1 诊断思路
【业务诊断】
列出包结构和主要类,按照业务维度进行诊断分析:
- 梳理业务组件:相关联的包或类归属到对应的组件下,根据用户的使用路径,查看这个类的依赖和引用情况;
- 梳理功能组件:查看前面业务组件多次引用的类,如果一个类被多个业务组件所引用,那么大概率这个类要被划分到功能组件的范围;
- 梳理技术组件:能不能放在任何一个应用里使用
【接口诊断】
根据类依赖和引用关系,找到业务边界类:
- 列出核心类的依赖和引用关系,Ctrl+鼠标左键找到引用类,逐个排查调用的地方,设计好业务间API接口
1.2 搜索诊断
包名 | 主要功能 | 核心类 | 类依赖及引用情况 | 重构优先级 | 组件划 | 重构操作 | 修改点 | 操作状态 |
---|---|---|---|---|---|---|---|---|
adapter | 主要存放列表的适配器 | DefaultRecyclerAdapter | 无依赖 | 1 | 基础组件 | 下沉 common | 可复用 | move-done |
1.3 业务诊断
包名 | 主要功能 | 核心类 | 类依赖及引用情况 | 重构优先级 | 组件划分 | 重构操作 | 修改点 | 操作状态 |
---|---|---|---|---|---|---|---|---|
activity | 存放数据模型、工具类、ViewModel、Activity |
2 组件化
3.1 准备工作
- 确保目标 Module 存在
若目标 Module 不存在,需先创建新 Module,创建步骤如下所示
如上图 File → New → New Module → Android Library
,并确保在 settings.gradle
中已包含:
php
include ':app', ':original_module', ':target_module'
include ':space'
- 在app中添加该Module,确保新增module会构建到工程中
scss
//build.gradle :app
implementation(':space')
- 复制相关依赖
为了避免迁移时AS报错导致移动失败,可以提前将原Module中的依赖拷贝到创建的Module,然后同步项目,如:
java
dependencies {
implementation project(':login')
implementation project(':common')
implementation project(':config')
implementation project(':common_ui')
}
确保可以迁移成功,后面再删除多余的依赖
- 更改targetSdk和JDK版本
kotlin
apply plugin: 'com.android.library'
apply from: "${rootProject.projectDir}/constants.gradle"
apply from: "${rootProject.projectDir}/versions.gradle"
apply from: "${rootProject.projectDir}/template/module.gradle"
apply from: "${rootProject.projectDir}/create_module_api.gradle"
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdk build_versions.compile_sdk
defaultConfig {
minSdkVersion build_versions.min_sdk
targetSdk build_versions.target_sdk
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
versionCode build_versions.version_code
versionName build_versions.version_name
archivesBaseName = prop_archivesBaseName
//指定room.schemaLocation生成的文件路径
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString(),
AROUTER_MODULE_NAME : project.getName(),
AROUTER_GENERATE_DOC : "enable"]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
kapt {
useBuildCache = true
javacOptions {
option("-Xmaxerrs", 500)
}
arguments {
arg("AROUTER_MODULE_NAME": project.getName())
}
}
flavorDimensions "versionCode", "area"
//根据配置项,代码中会有一些差别
productFlavors {
//
}
dataBinding {
enabled = true
}
}
- 引入apply from : " ${ rootProject . projectDir } /create_module_api.gradle", 提供对外的接口
3.2 接口替换
- 如1.1和1.2中的表格,根据诊断结果,先定义好对外的接口,各个引用实例调用的地方都需要核查到
- 根据定义好的接口生成space-api.jar
- 找到原来引用的地方,进行接口替换
3.3 移动类文件
记得按照小步快跑,逐步验证的原则
- 首先拷贝所有资源
- 考虑到多语言,res下的资源可以通过剪切的方式拷贝过去,这样的好处是既快又全,且能保留git提交记录,当类文件移动过来时,资源是全的。
- 类移动完成编译验证ok后,用不上的再从两边Module中删除掉
- 移动类文件
方法一:
经过测试,简单快捷的方式是:在AS中将所有需要的类一次性剪切到新的module,既能最大程度避免import类重定向的问题,也能保留git提交记录,是目前测试到的最优方式。
方法二:
- 使用 Refactor → Move(推荐,自动处理部分依赖)
按照诊断的结论,根据优先级来进行移动,优先级数字越小,表示越高优,则优先重构
- 右键点击要移动的类文件,选择
Refactor → Move...
(或按F6
)。 - 在弹出窗口中:
- To directory :选择目标 Module 的包路径(如
target_module/src/main/java
)。 - Search for references:勾选此项,自动修正原 Module 中的引用。
- To directory :选择目标 Module 的包路径(如
- 确认移动 :
点击Refactor
,Android Studio 会尝试自动更新代码中的引用(但可能仍需手动检查)。
- 迁移完记得在git-Local Changes中解决引用错误,由于类都是move过来的,将引用还原回去,就可以解决大部分问题
方式二只适用于小范围重构
3.4 解决编译错误
- 清理项目:
Build → Clean Project
→Build → Rebuild Project
。 - 手动修正错误:
-
- 包名错误 :检查所有
import
语句是否指向新包名。 - 缺失依赖 :若目标 Module 需要第三方库,需在其
build.gradle
中添加依赖。
- 包名错误 :检查所有
这步会比较耗时,可以在Git---Local Changes逐个浏览更改
3.5 验证功能
- 启动 App:手动测试相关功能是否正常。
- 检查日志 :关注运行时是否抛出
ClassNotFoundException
或资源加载错误。
验证功能的过程中会遇到各种忽略的奇怪错误,保持耐心,逐个解决即可,常见的错误见3.6节
3.6 常见问题与解决
- 问题1 Cannot resolve type 'controller'
【原因】 类的路径变动后,DataBinding定义的类路径并不会自动修正
- 问题2 Found UnSupported class type
【原因】由于类都是剪切过来的,引用的路径会有错误的情况,继承com.alibaba.android.arouter.facade.template.IProvider的接口中有类引用路径错误
- 问题3 Didn't find class "com. ..PictorialApplication"
【原因】build缓存文件未清掉,导致编译时异常,记得将clean project,并将未clean掉的build目录都删除掉
- 问题4 ARouter$$Group is defined multiple times
【原因】由于类的迁移未更改Route path或name的前缀,不同的module中,使用了相同的前缀,导致出现如上错误
- ProGuard混淆的规则记得同步更改
3.7 最佳实践
- 逐步操作:将整个流程进行解耦,每一步想清楚弄完后再进行下一步,逐步验证,这样即使有问题也是小问题,很容易排查,避免大规模改动后难以定位问题。
- 优先使用 Refactor 工具:减少手动操作导致的遗漏。
- 模块职责单一化:确保目标 Module 的职责清晰,移动的过程中难免有遗漏,记得做好注释,后续很快可以找到解决
3 解耦重构
3.1 接口优化
在组件化拆分完成后,根据第1章中诊断分析的现状,继续分析当前类的依赖和引用是否合理,进行组件内重构: 详见1.3中的表格
4 历史缺陷分析
4.1 缺陷分析
取2024.1-2024.12的缺陷进行分析如下:
4.2 代码优化
根据bug产生的原因,从代码角度最大程度规避问题:
缺陷类型 | 原因 | 解决方法 |
---|---|---|
跳转index错误 | 处理的地方有多处,且处理的代码样式不一样,会漏掉 | 将关键的判断方法抽取为统一的方法,使任何人添加代码都不会出错 |
5 指导手册
从测试和开发角度进行分析,为了提高效率,输出指导手册