前言
移動端平台不斷發展,不斷迭代更新,APP軟件越來越復雜和龐大,維護和更新亦是如此。為了解決這些問題,降低軟件的復雜性和耦合度,同時提高開發效率,模塊化在移動端就變得勢在必行。
模塊化理解
模塊化是指解決一個復雜問題時自頂向下逐層把系統划分成若干模塊的過程。每個模塊完成一個特定的子功能,所有的模塊按某種方法組裝起來,成為一個整體,完成整個系統所要求的功能。
通過以下類比可以更好地理解什么是模塊化:
我們可以把軟件看做是一輛汽車,開發一款軟件的過程就是生產一輛汽車的過程。一輛汽車由車架、發動機、變數箱、車輪等一系列模塊組成;同樣,一款大型商業軟件也是由各個不同的模塊組成的。
汽車的這些模塊是由不同的工廠生產的,一輛 BMW 的發動機可能是由位於德國的工廠生產的,它的自動變數箱可能是 Jatco(世界三大變速箱廠商之一)位於日本的工廠生產的,車輪可能是中國的工廠生產的,最后交給華晨寶馬的工廠統一組裝成一輛完整的汽車。這就類似於我們在軟件工程領域里說的多團隊並行開發,最后將各個團隊開發的模塊統一打包成我們可使用的 App 。
一款發動機、一款變數箱都不可能只應用於一個車型,比如同一款 Jatco 的 6AT 自動變速箱既可能被安裝在 BMW 的車型上,也可能被安裝在 Mazda 的車型上。這就如同軟件開發領域里的模塊重用。
到了冬天,特別是在北方我們可能需要開着車走雪路,為了安全起見往往我們會將汽車的公路胎升級為雪地胎;輪胎可以很輕易的更換,這就是我們在軟件開發領域談到的低耦合。一個模塊的升級替換不會影響到其它模塊,也不會受其它模塊的限制;同時這也類似於我們在軟件開發領域提到的可插拔。
模塊化優缺點
優點
- 架構靈活,焦點分離
- 耦合低,模塊間可自由組合、分解
- 方便單個模塊功能調試、升級、測試,提升開發效率
- 多人協作只負責單獨模塊,互不干擾
缺點
- 系統分層,調用鏈會很長
- 模塊間發送消息對比較損耗性能
模塊化分層
組件和模塊區別定義
- 組件:指的是單一的功能組件,地圖組件、支付組件、分享組件等功能;
- 模塊:指的是獨立的業務模塊,如首頁模塊、聊天模塊、播放模塊等,模塊相對於組件來說粒度更大。
整個項目分為四層,從下至上分別是:
(1)宿主層:不做具體的項目功能實現,只負責集成業務模塊,組裝成一個完整的APP;
(2)業務模塊層:將項目的每個大功能模塊拆分成的一個一個單獨的module,可獨立運行,同產品的不同項目也可復用;
(3)業務組件層:用於業務模塊間調用,例如支付組件 、地圖組件、分享組件等等;
(4)基礎組件層:基礎組件層,與業務無關,與項目也無關,所有項目都可以全部復用,包含了各種開源庫以及和業務無關的各種自研工具庫;
模塊化分層,其實就是將業務模塊層的各個功能業務拆分層獨立的業務模塊;進行模塊化的第一步就是業務模塊划分,划分的粒度需要根據項目情況進行合理把控,這就需要對業務和項目有較為透徹的理解。
模塊化過程
1、每個單獨的Module 都可以單獨作為Application編譯成 APK運行,同時也可以作為依賴包Library來整體編譯打包。於是,需要在每個Module下的 build.gradle
中加入如下代碼:
if (isModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
isModule作為開關去控制是否為Application后者Library,只需要在項目gradle.properties
文件中加入如下配置:
isModule=false
2、作為Application時清單文件需要設置Application屬性及啟動Activity等,而Library則簡單很多,因此就需要兩套不同的AndroidManifest.xml
。同樣,只要在每個Module下的 build.gradle
的android配置中加入如下設置:
sourceSets {
main {
if (isModule.toBoolean()) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
//release模式下排除debug文件夾中的所有Java文件
java {
exclude 'debug/**'
}
}
}
}
保持原先拷貝一份AndroidManifest.xml
到新建的debug目錄下即可,如圖所示:
main目錄下作為Library清單文件:
<application>
<activity android:name=".BottomAlignmentActivity" />
</application>
debug目錄下作為Application中清單文件:
<application
android:icon="@drawable/ic_author"
android:label="@string/advancedtextview_app_name"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity
android:name=".BottomAlignmentActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
3、宿主APP在Application和Library模式下,進行不同依賴配置,:
if (!isModule.toBoolean()) {
implementation project(path: ':advancedtextview')
}else{
implementation project(":libbase")
}
4、模塊間跳轉
宿主工程中依賴的庫是可以直接引用的,通過startActivity
跳轉,但組件之間不可以依賴。因此,當常規業務模塊之間需要跳轉引用,改如何處理呢?
- 隱式Intent,要跳轉的活動在
Manifest.xml
中聲明匹配規則,然后調用
Intent intent = new Intent(Intent.ACTION_VIEW, "<scheme>://<host>:<port>/<path>");
startActivity(intent);
- 利用反射
Class clazz=Class.fromName("com.nianlun.expample.MainActivity");
startActivity(this,clazz);
- 使用路由,這里推薦阿里的ARouter
一個用於幫助 Android App 進行組件化改造的框架 —— 支持模塊間的路由、通信、解耦。
Arouter使用
-
添加依賴和配置
android { defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = [AROUTER_MODULE_NAME: project.getName()] } } } } dependencies { api 'com.alibaba:arouter-api:x.x.x' annotationProcessor 'com.alibaba:arouter-compiler:x.x.x' ... }
-
添加注解
// 在支持路由的頁面上添加注解(必選) // 這里的路徑需要注意的是至少需要有兩級,/xx/xx @Route(path = "/test/activity") public class YourActivity extend Activity { ... }
-
初始化SDK
if (isDebug()) { // 這兩行必須寫在init之前,否則這些配置在init過程中將無效 ARouter.openLog(); // 打印日志 ARouter.openDebug(); // 開啟調試模式(如果在InstantRun模式下運行,必須開啟調試模式!線上版本需要關閉,否則有安全風險) } ARouter.init(mApplication); // 盡可能早,推薦在Application中初始化
-
發起路由操作
// 1. 應用內簡單的跳轉(通過URL跳轉在'進階用法'中) ARouter.getInstance().build("/test/activity").navigation(); // 2. 跳轉並攜帶參數 ARouter.getInstance().build("/test/1") .withLong("key1", 666L) .withString("key3", "888") .withObject("key4", new Test("Jack", "Rose")) .navigation();
更多進階用法,可以跳轉官方GitHub網站進行文檔查看:https://github.com/alibaba/ARouter/blob/master/README_CN.md
問題解決
資源名沖突的問題,可以通過在 build.gradle
定義前綴的方式解決:
defaultConfig {
...
resourcePrefix "module_name_"
...
}
參考資料:Android 模塊化探索與實踐
以上就是模塊化的基本過程和實現,具體項目中我們還是要理清業務之間的關系,進行具體划分,提取公共依賴,剔除冗余代碼,逐步進行重構。
借用以前的代碼例子,模塊化處理了一下,采用Arouter進行跳轉,具體可以查看我的Github查看,地址如下
https://github.com/MickJson/DevelopmentRecord
歡迎點擊查閱及Star,我也會繼續補充其它有用的知識及例子在項目上。
歡迎點贊/評論,你們的贊同和鼓勵是我寫作的最大動力!