Android组件化实践
1 目的以及组件化与模块化区分
参考自:分析组件化与模块化之间的区别
组件化:把重复使用的代码拆分成一个个组件,提供给功能使用
模块化:按照项目功能需求,将代码拆分成一个个模块。
区别:组件化是为了重用而拆分模块, 模块化是为了业务分离而拆分模块
安卓组件化示意图:
在开发阶段组件1、组件2、组件3都可以单独作为一个应用独立运行,上线时组件1、组件2、组件3 + App壳 + 依赖的其他模块共同构成应用。
2 组件化具体操作
2.1 统一编译工具版本号以及依赖版本号
(1) 统一编译工具版本号
在项目根目录build.gradle中增加编译工具配置
...
ext {
...
sdkID = [
"compileSdkVersion": 30,
"buildToolsVersion": "30.0.1",
"minSdkVersion" : 21,
"targetSdkVersion" : 30,
]
}
在各模块中引用,如base模块的build.gradle引用(其他模块同理)
plugins {
id 'com.android.library'
}
android {
compileSdkVersion rootProject.ext.sdkID.compileSdkVersion
buildToolsVersion rootProject.ext.sdkID.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.sdkID.minSdkVersion
targetSdkVersion rootProject.ext.sdkID.targetSdkVersion
versionCode 1
versionName "1.0"
}
...
}
...
(2) 统一项目依赖版本号
同样,在项目根目录build.gradle中增加依赖配置
...
ext {
...
dependenciesID = [
"appcompat" : "androidx.appcompat:appcompat:1.2.0",
"material" : "com.google.android.material:material:1.2.1",
"constraintlayout" : "androidx.constraintlayout:constraintlayout:2.0.4",
"recyclerview" : "androidx.recyclerview:recyclerview:1.1.0"
]
}
在各模块中引用,如base模块的build.gradle中如下引用:
...
dependencies {
implementation rootProject.ext.dependenciesID.appcompat
implementation rootProject.ext.dependenciesID.material
implementation rootProject.ext.dependenciesID.constraintlayout
}
2.2 组件化配置
(1) 在根目录build.gradle中增加变量isRunTotalApp,用于指示各组件是否作为library
ext {
...
//true 表示各组件共同构成一个应用, false表示各组件可单独成为应用
isRunTotalApp = false
}
(2)在App壳中配置对module1的引用
...
dependencies {
...
if (isRunTotalApp) {
implementation project(":module1")
}
}
...
(3)在组件module1对应的build.gradle中进行如下配置, 指定应用是否作为library、应用id、应用对应的AndroidManifest.xml文件
if (isRunTotalApp) {
//整体作为一个应用时作为library
apply plugin : "com.android.library"
} else {
//单独运行时作为application
apply plugin : "com.android.application"
}
android {
compileSdkVersion rootProject.ext.sdkID.compileSdkVersion
buildToolsVersion rootProject.ext.sdkID.buildToolsVersion
defaultConfig {
if (!isRunTotalApp) {
//单独运行时指定应用id
applicationId "com.example.module1"
}
minSdkVersion rootProject.ext.sdkID.minSdkVersion
targetSdkVersion rootProject.ext.sdkID.targetSdkVersion
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
main {
if (!isRunTotalApp) {
// 单独运行时指定AndroidManifest.xml文件
manifest.srcFile 'src/main/test/AndroidManifest.xml'
} else {
// 作为一个组件时指定AndroidManifest.xml文件, 默认AndroidManifest.xml文件
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
...
单独运行时需要在main/test文件夹下创建AndroidManifest.xml文件,指定组件单独运行时的入口。
sync项目后即可在运行窗口中看到module1组件,需要时可以将isRunTotalApp设置为true运行整个项目。
3 组件间跳转: 使用ARouter使用组件间跳转
再次创建一个组件module2, 和module1配置一样,
通常而言, 可以通过隐式intent来实现组件间的跳转,还有就是通过路由的方式实现组件间跳转,如使用ARouter框架。
相关配置参考:ARouter、一个组件化工程模板(kotlin,androidX,ARouter)、androidX与support-v4包冲突问题
3.1 ARouter配置
1、在项目根目录下的build.gradle文件中增加ARouter依赖
...
ext {
arouter_compiler_version = "1.5.1"
arouter_api_version = "1.5.1"
dependenciesID = [
...
"arouterApi" : "com.alibaba:arouter-api:${arouter_api_version}",
"arouterCompiler" : "com.alibaba:arouter-compiler:${arouter_compiler_version}",
...
]
sdkID = [
"compileSdkVersion": 30,
"buildToolsVersion": "30.0.1",
"minSdkVersion" : 21,
"targetSdkVersion" : 30,
]
//true 表示各组件共同构成一个应用, false表示各组件可单独成为应用
isRunTotalApp = true
}
2、在app壳中配置ARouter的依赖以及需要的AROUTER_MODULE_NAME属性
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
...
}
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
dependencies {
...
implementation(rootProject.ext.dependenciesID.arouterApi)
kapt rootProject.ext.dependenciesID.arouterCompiler
...
if (isRunTotalApp) {
implementation project(":module1")
implementation project(":module2")
}
}
...
在module1以及module2的build.gradle文件中同样配置。
3、在应用的Application中调用ARouter.init方法初始化ARouter。
package com.example.myapplication
import android.app.Application
import android.content.Context
import com.alibaba.android.arouter.launcher.ARouter
class BaseApplication : Application() {
companion object {
lateinit var context : Context
}
override fun onCreate() {
super.onCreate()
context = this
ARouter.init(this)
}
}
在需要启动的activity上增加注解,配置路径,用来进行跳转。
@Route(path = "/module2/MainModule2Activity", group = "module2")
public class MainModule2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
String from = getIntent().getStringExtra("from");
Toast.makeText(this, String.valueOf(from), Toast.LENGTH_SHORT).show();
}
}
使用如下代码即可实现跳转,并传递from数据:
ARouter.getInstance().build("/module2/MainModule2Activity")
.withString("from", "" + getClass().getName())
.navigation(this);
3.2 ARouter依赖的support包与androidX冲突问题
在gradle.properties中添加如下属性(参考自ARouter的一个issue以及AndroidX概览):
android.enableJetifier=true