什么是组件化?
组件化: 就是将一个 Application 的开发分成多个模块,每个模块都是一个组件(Module),开发的过程中我们可以只用专注自己开发的模块,进行单独调试,但在最终发布 apk 时,又可以将这些组件合并成一个统一的 app。
组件化环境搭建
实现组件化基本思路: 根据配置文件中的 isAloneRun 变量来决定该组件是作为 module 工程集成到 App 工程中,还是单独作为 app 工程独立运行。
组件作为 module 和 作为独立的 App 的不同的配置主要有:
- 组件的 build.gradle 文件配置:作为 library 时配置为
apply plugin: 'com.android.library'
作为 app 时配置为apply plugin: 'com.android.application'
; - 组件的 AndroidManifest.xml 文件配置: 作为 library 时,AndroidManifest.xml 文件中的 标签不配置 android:applicationName 属性;
common_base 模块主要负责封装公共功能,例如 网络请求框架,图片加载框架,各种 utils ,为了防止重复依赖库的问题,所有的依赖库都放在该模块中进行加载,其他业务组件不做第三方库的依赖,只需依赖 common_base 公共库即可。
common_base 模块无论是在什么情况下都是作为 library 的形式存在的 ,其他的组件依赖它。
第一步: 为了方便管理第三方库的版本,在 project 目录下新建一个 config.gradle 文件(直接复制一份 build.gradle), 配置 SDK 版本和所用到的第三方库的版本,内容如下:
/**
引用步骤:
(1) 在 project 下的 build.gradle 文件第一行添加 apply from : "config.gradle"
(2) 在各个 module 中用 implemetions rootProject.ext.dependencies['xxx'] 和 annotationProcessor rootProject.ext..dependencies['xxx']
**/
ext {
android = [
applicationId : 'com.xing.componentsample',
compileSdkVersion: 28,
buildToolsVersion: "28.0.3",
minSdkVersion : 15,
targetSdkVersion : 28,
versionCode : 1,
versionName : "1.0"
]
dependencies = [
// support
"appcompat-v7" : "com.android.support:appcompat-v7:28.0.0",
"design" : "com.android.support:design:28.0.0",
"support-v4" : "com.android.support:support-v4:28.0.0",
"cardview-v7" : "com.android.support:cardview-v7:28.0.0",
"annotations" : "com.android.support:support-annotations:28.0.0",
"recyclerview-v7" : "com.android.support:recyclerview-v7:28.0.0",
"gson" : "com.google.code.gson:gson:2.2.4",
"butterknife" : "com.jakewharton:butterknife:8.8.1",
"butterknife-compiler": "com.jakewharton:butterknife-compiler:8.8.1",
"constraint-layout" : "com.android.support.constraint:constraint-layout:1.1.3",
"retrofit" : "com.squareup.retrofit2:retrofit:2.4.0",
"rxandroid" : "io.reactivex.rxjava2:rxandroid:2.1.0",
"rxjava" : "io.reactivex.rxjava2:rxjava:2.2.2",
"converter-gson" : "com.squareup.retrofit2:converter-gson:2.4.0",
"adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:2.4.0",
"okhttp" : "com.squareup.okhttp3:okhttp:3.11.0",
"logging-interceptor" : "com.squareup.okhttp3:logging-interceptor:3.11.0",
"arouter-api" : "com.alibaba:arouter-api:1.4.1",
"arouter-compiler" : "com.alibaba:arouter-compiler:1.2.2"
]
}
在 project 中的 build.gradle 文件中引入该 config.gradle 配置文件:
apply from: "config.gradle"
第二步: 在 common_base 模块中引入第三方库:
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
api rootProject.ext.dependencies['appcompat-v7']
api rootProject.ext.dependencies['design']
api rootProject.ext.dependencies['support-v4']
api rootProject.ext.dependencies['cardview-v7']
api rootProject.ext.dependencies['annotations']
api rootProject.ext.dependencies['recyclerview-v7']
api rootProject.ext.dependencies['constraint-layout']
api rootProject.ext.dependencies['gson']
api rootProject.ext.dependencies['constraint-layout']
api rootProject.ext.dependencies['retrofit']
api rootProject.ext.dependencies['rxandroid']
api rootProject.ext.dependencies['rxjava']
api rootProject.ext.dependencies['converter-gson']
api rootProject.ext.dependencies['adapter-rxjava']
api rootProject.ext.dependencies['okhttp']
api rootProject.ext.dependencies['logging-interceptor']
// butterknife
api rootProject.ext.dependencies['butterknife']
annotationProcessor rootProject.ext.dependencies['butterknife-compiler']
// arouter
api rootProject.ext.dependencies['arouter-api']
}
注意此处使用 api 而不是 implementions ,否则其他业务组件将引用不到这些第三方库。
第三步: 在 project 的 gradle.properties 文件中新增 isRunAlone 变量
# 标示各个业务组件是否以 application 单独运行,修改后需要同步才能生效
isRunAlone=false
在业务组件的 build.gradle 文件中,添加 common_base 公共库的依赖 ,并根据 isRunAlone 变量配置 apply plugin 是 application 还是 library , 以及加载不同的 AndroidManifest.xml 文件,配置内容如下:
// module_login/build.gradle 文件
if (isRunAlone.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
apply plugin: 'com.jakewharton.butterknife'
android {
// 也可以使用这种方式:rootProject.ext.android['compileSdkVersion']
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
includeCompileClasspath = true
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
if (isRunAlone.toBoolean()) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
// 全部 module 一起编译时剔除 debug 目录
exclude '**/debug/**'
}
}
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
// 依赖公共库
implementation project(':common_base')
// butterknife-compiler
annotationProcessor rootProject.ext.dependencies['butterknife-compiler']
// arouter-compiler
annotationProcessor rootProject.ext.dependencies['arouter-compiler']
}
其中 AndroidManifest.xml文件的配置,在作为 library 时,不配置 applicationName 和 程序入口 Activity(如果整个 App 的入口 Activity 不是该 Activity )
<!--作为组件module被加载时,Manifest.xml中不能配置 application name -->
<application>
<activity
android:name=".activity.LoginActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.RegisterActivity"
android:screenOrientation="portrait" />
</application>
<!--作为 app 时,需要配置 application name 和程序入口-->
<application
android:name=".app.LoginModuleApplication"
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="@drawable/login_ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".activity.LoginActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.RegisterActivity"
android:screenOrientation="portrait" />
</application>
第四步: 各组件之间的跳转,由于各个组件之间没有依赖关系,所以通常需要借助 路由 来做跳转。
这里借助阿里 ARouter 路由;
gradle 配置:
android {
defaultConfig {
........
// Arouter路由配置
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
includeCompileClasspath = true
}
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
// 依赖公共库
implementation project(':common_base')
// arouter-compiler
annotationProcessor rootProject.ext.dependencies['arouter-compiler']
}
开始跳转页面:
/**
* 跳转主界面
*/
private void gotoMainActivity() {
ARouter.getInstance().build("/main/activity").navigation();
finish();
}
目标页面:
@Route(path = "/main/activity")
public class MainActivity extends BaseActivity {
........
}
集成注意点
ButterKnife 问题
在 project 的 build.gradle 添加 butterknife plugin,然后在所有用到 butterknife 的 library 中添加如下配置
apply plugin: 'com.jakewharton.butterknife'
...........
dependencies {
// butterknife-compiler
annotationProcessor rootProject.ext.dependencies['butterknife-compiler']
}
在 library 中集成 butterknife,绑定控件时,需要使用 R2 代替 R, 以及在 library 中控件的 id 不是常量,所以在不能使用 switch 语句。
资源文件命名冲突
为了避免资源文件冲突问题,最好以该模块名为文件前缀进行区分。
源码地址: https://github.com/xing16/ComponentSample