Android 元件化必知必會
什麼是元件化?
元件化: 就是將一個 Application 的開發分成多個模組,每個模組都是一個元件(Module),開發的過程中我們可以只用專注自己開發的模組,進行單獨除錯,但在最終釋出 apk 時,又可以將這些元件合併成一個統一的 app。
元件化環境搭建
實現元件化基本思路: 根據配置檔案中的 isAloneRun 變數來決定該元件是作為 module 工程整合到 App 工程中,還是單獨作為 app 工程獨立執行。
元件作為 module 和 作為獨立的 App 的不同的配置主要有:
- 元件的 build.gradle 檔案配置:作為 library 時配置為
apply plugin: 'com.android.library'
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 語句。
資原始檔命名衝突
為了避免資原始檔衝突問題,最好以該模組名為檔案字首進行區分。