开发中,我习惯性会把一个模块的功能放在一个包下,便于查找,但烦于耦合性太高,后期维护太费劲,因此对项目进行组件化拆分势在必行。组件化好处:便于开发,团队成员只关注自己的开发的小模块,降低耦合性,后期维护方便等。相当于先有很多小组件,各自开发,最后组装,成一个 app。
app:壳工程; module1:组件 1 ; module2:组件 2 ; resource:专门放资源文件; router:路由,所有页面请求都由它中转; common:第三方库,公用工具、自定义 View 等。
组件化过程很容易想到一些问题,比如 module1 我想单独调试怎么做? module1 有页面需要跳转到 module2 怎么办等。接下来,我一一探索,提供解决方案。
如果有很多项目,可以设置全局来统一管理版本号或依赖库,这样就不用一个个去改了,根目录下 build.gradle 添加:
def androidSupportVersion = '27.1.0'
ext {
//编译的 SDK 版本,如 API20
compileSdkVersion = 27
//构建工具的版本,其中包括了打包工具 aapt、dx 等,如 API20 对应的 build-tool 的版本就是 20.0.0
//buildToolsVersion = "26.0.0"
//兼容的最低 SDK 版本
minSdkVersion = 14
//向前兼容,保存新旧两种逻辑,并通过 if-else 方法来判断执行哪种逻辑
targetSdkVersion = 27
appcompatV7 = "com.android.support:appcompat-v7:$androidSupportVersion"
constraintLayout = 'com.android.support.constraint:constraint-layout:1.0.2'
}
其中 module/build.gradle:
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
//……
}
每个 module 都有 app_name,为了不让资源名重名,可以在每个组件的 build.gradle 中增加 resourcePrefix "xxx_",固定每个组件的资源前缀。但是 resourcePrefix 这个值只能限定 xml 里面的资源,并不能限定图片资源,所有图片资源仍然需要你手动去修改资源名。~~不过我更建议把图片、strings、colors、dimens 等资源放到 common 去,可以防止不同的资源名字却对应了同一资源值。 为什么叫 common,任何项目都能用,开发中发现把资源文件放在 common 明显不合理,专门建了个 resource 放资源文件,因此 resourcePrefix "xxx_" 也变得意义不大了。另外各个 module 中的 layout 的名字可能一样,其实我们按照一个规则来,比如只有一个 MainActivity,自然对应的 layout 名不一样。~~ PS:我最后还是用的前一种方案,这里是要权衡资源有可能重复还是能让每个模块都能独立运行资源更加清晰?
module1 在开发阶段应该 application,等 release 后才是 library,这里可以设置一个变量控制下,在根项目 gradle.properties 加入:
# 组件单独调试开关,true 可以,false 不可以,需要点击 "Sync Project"。
isDebug=false
module1/build.gradle:
if (isDebug.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
//……
}
开发阶段,module1 还必须有个 applicationId:
android {
//……
defaultConfig {
// 作为 library 时不能有 applicationId,只有作为一个独立应用时才能够如下设置
if (isDebug.toBoolean()){
applicationId "com.wuxiaolong.module1"
}
//……
}
}
到这里还不行,还得有 AndroidManifest 设置入口类,release 后这个 AndroidManifest 不需要打包进去,新建文件 debug,然后在 build.gradle 指定路径:
android {
//……
sourceSets {
main {
if (isDebug.toBoolean()) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/release/AndroidManifest.xml'
java {
//release 时 debug 目录下文件不需要合并到主工程
exclude 'debug/**'
}
}
}
}
}
另外,module 可能会需要使用到自定义的 Application,release 同样也不需要打包进去,不然合并会有冲突。
组件间通信包括两个场景:( 1 ) UI 跳转;( 2 )调用组件某个类的某个方法。 这里涉及路由,何为路由,就是页面请求,都交给它处理。网上有很多路由库,我这里选的是阿里的 ARouter,ARouter 能解决上面的问题,但是也遗留一个问题,我独立运行 module1 时,想访问 module2 页面就做不到了,Router 不支持跨进程访问,这个问题待定,也可能是我使用 ARouter 姿势不对,如果您能做到,望告知。
1、common
dependencies {
//arouter
compile rootProject.ext.arouterApi
}
2、组件 app 和 module 都需要加入:
android {
defaultConfig {
//arouter
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
dependencies {
//arouter
annotationProcessor rootProject.ext.arouterCompiler
}
3、使用 sample 列出了组件跳转、组件跳转-带参数、获取 Frgment、调用组件某个类的使用方法,详见我的 GitHub 分享。 ARouter 详细使用请阅读 ARouter,不得不吐槽,文档写的不是一般的烂。
module1 和 module2 分别都依赖了 common,会不会导致 library 重复依赖呢,想必大家也有这个疑问了,实际上在 release 构建 APP 的过程中 Gradle 会自动将重复的 aar 包排除,APP 中也就不会存在相同的代码了,可以打包反编译验证下,我试了,确实没有重复依赖。
在 Android Studio 的 library 的 module 中无法使用 ButterKnife。 网上说用 R2 替代(为什么能用 R2 ?),但都没有说 R2 怎么生成的?这篇《 butterknife 在 library 中使用问题处理》文章说使用 android-apt,确实可行,但是带来一个新坑,发现 apply plugin: 'android-apt' 与 arouter 冲突,这时候 arouter 失效了。正确姿势,用 Android ButterKnife Zelezny 插件生成,手动改成 R2,clean 下就 OK,感谢群里的小伙伴提示。
ButterKnife 还有个坑,OnClick 方法中同样使用 R2,但是找 id 的时候使用 R,然而 library 中是不能使用 switch- case 找 id 的(原因:《在 Android library 中不能使用 switch-case 语句访问资源 ID 的原因分析及解决方案》),可以使用 if-else:
@OnClick({R2.id.module1_button, R2.id.module1_button2})
public void onViewClicked(View view) {
int id = view.getId();
Log.d("wxl","id="+id);
if (id == R.id.module1_button) {
toastShow("module1_button");
} else if (id == R.id.module1_button2) {
toastShow("module1_button2");
}
}
当你写 switch- case 时,Android Studio 也有提示,可以一键转换成 if-else。
https://github.com/WuXiaolong/ModularSample
我的微信公众号:吴小龙同学,欢迎关注交流~
1
tanranran 2018-09-18 22:31:33 +08:00
赞一个
|
2
shangshicc 2018-09-18 22:59:19 +08:00
如果多个 module 中都用到同一个 dialog,你会把 dialog 抽出来放到 common 里面还是通过 ARouter 回调
|
3
davy1995 2018-09-19 00:51:22 +08:00 via Android
组件化还是用 dragger2 吧
|