3、Gradle 组件化配置:多Module项目搭建,组件独立运行与集成模式切换,资源命名规范
好,咱们今天来聊聊组件化落地最核心的一步——Gradle 配置。
说实话,很多同学看组件化的文章,理论都懂,一动手就懵。为什么?因为 Gradle 脚本没写好。我见过太多项目,组件化搞了一半,结果编译脚本乱成一锅粥,最后又退回单模块。嗯,今天我就把我在几个大型项目里踩过的坑、总结出的套路,一次性讲清楚。
3.1 多Module项目结构设计
先说说项目长什么样。我个人习惯,一个组件化项目至少分三层:
- app壳工程:只负责组装,不写业务代码
- 业务组件层:比如首页、发现、我的,每个都是一个独立Module
- 基础组件层:网络库、图片加载、工具类,被所有业务组件依赖
你想想看,如果所有代码都堆在app模块里,那还叫什么组件化?
举个例子,我最近重构的一个项目,目录结构是这样的:
MyApp/
├── app/ # 壳工程
├── component_home/ # 首页组件
├── component_discover/ # 发现组件
├── component_mine/ # 我的组件
├── lib_network/ # 网络库
├── lib_image/ # 图片加载
├── lib_common/ # 公共工具
└── settings.gradle.kts # 模块注册文件
这里有个细节:业务组件我统一用 component_ 前缀,基础库用 lib_ 前缀。为什么?因为我在项目中遇到过,有人把业务组件和基础库混在一起,找起来特别费劲。命名规范,从一开始就要定好。
3.2 组件独立运行与集成模式切换
这是组件化最核心的痛点。每个组件在开发时,要能独立运行、独立调试;在集成时,又要能作为library被app壳工程依赖。
怎么做?说白了,就是靠一个开关变量来控制。
我在根项目的 gradle.properties 里加了一个配置:
# 组件独立运行开关
# true = 独立运行模式,false = 集成模式
isRunAlone=false
然后在每个业务组件的 build.gradle.kts 里,根据这个变量动态切换:
if (isRunAlone.toBoolean()) {
// 独立运行模式:作为application
apply(plugin = "com.android.application")
} else {
// 集成模式:作为library
apply(plugin = "com.android.library")
}
android {
// 注意:applicationId 只在独立运行时生效
if (isRunAlone.toBoolean()) {
defaultConfig {
applicationId = "com.example.component_home"
}
}
// 独立运行时需要单独的AndroidManifest
sourceSets {
getByName("main") {
if (isRunAlone.toBoolean()) {
manifest.srcFile("src/main/manifest/AndroidManifest.xml")
} else {
manifest.srcFile("src/main/AndroidManifest.xml")
}
}
}
}
核心要点:独立运行时,组件需要自己的Application类和启动Activity。集成模式下,这些都要去掉,只保留library的清单文件。
我曾经犯过一个错:独立运行模式下的Application类里,初始化了某个第三方SDK。结果集成到主工程后,那个SDK被初始化了两次,直接崩溃。嗯,后来我学乖了,在独立运行的Application里加了一个判断:
class HomeApplication : Application() {
override fun onCreate() {
super.onCreate()
// 只在独立运行时初始化
if (isRunAlone.toBoolean()) {
initSDK()
}
}
}
3.3 资源命名规范
这个坑,我估计每个搞组件化的人都踩过。资源名冲突,编译时不会报错,运行时直接覆盖。你想想看,两个组件都定义了 R.string.app_name,最后显示的是哪个?完全看运气。
我的解决方案很简单:强制前缀。
| 资源类型 | 命名规范 | 示例 |
|---|---|---|
| 布局文件 | 组件名_功能_描述 | home_activity_main.xml |
| 字符串 | 组件名_功能_描述 | home_title_welcome |
| 颜色 | 组件名_功能_描述 | home_bg_primary |
| 图片 | 组件名_功能_描述 | home_ic_avatar |
你可能会问:这样命名会不会太长了?
嗯,确实长。但比起排查资源冲突时那种抓狂的感觉,我宁愿多敲几个字符。而且,Android Studio 的代码补全功能,你输入 home_ 就能过滤出所有首页组件的资源,反而更高效。
小技巧:可以在每个组件的 build.gradle.kts 里配置 resourcePrefix,强制约束资源命名:
android {
resourcePrefix = "home_"
}
这样如果你不小心写了个没有前缀的资源,编译时直接报错。从源头杜绝问题。
3.4 依赖管理与版本统一
组件多了,依赖版本不一致的问题就来了。A组件用OkHttp 4.9,B组件用OkHttp 4.10,最后打包进去两个版本,apk体积变大不说,还可能出兼容性问题。
我的做法是:在根项目建一个 build.gradle.kts 或者 libs.versions.toml,统一管理所有依赖版本。
举个例子,用 libs.versions.toml 的方式:
[versions]
kotlin = "1.9.0"
okhttp = "4.12.0"
retrofit = "2.9.0"
[libraries]
okhttp-core = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
然后在每个组件的 build.gradle.kts 里引用:
dependencies {
implementation(libs.okhttp.core)
implementation(libs.retrofit.core)
}
这样,所有组件的依赖版本都统一了。想升级?改一个地方就行。
注意:千万不要在子模块里直接写死版本号。我曾经接手过一个项目,每个模块都写了自己的版本,升级时改了十几个文件,还漏了两个。那感觉,别提多酸爽了。
3.5 总结一下
组件化的 Gradle 配置,说白了就三件事:
- 结构清晰:业务组件和基础库分开,命名有规律
- 模式切换:用一个开关控制独立运行还是集成,manifest和Application要处理好
- 资源规范:强制前缀,统一版本,从源头避免冲突
这些配置看起来琐碎,但都是我在实际项目中一点一点试出来的。你照着做,至少能省掉80%的组件化配置坑。下一章,咱们聊聊组件间的通信——ARouter 的实战用法,那才是真正让组件「活」起来的关键。
公众号:蓝海资料掘金营,微信deep3321