元件化專案,通過gradle指令碼,實現module在編譯期隔離,執行期按需載入,實現元件間解耦,高效單獨除錯。
本專案github地址
github.com/wang7096939… 先來一張效果圖,建議讀者clone專案後跟著專案看這篇文章,有任何不明白的地方可留言或者聯絡我,我看到後會立刻回覆你。
元件化初衷
- APP版本不斷的迭代,新功能的不斷增加,業務也會變的越來越複雜,維護成本高。
- 業務耦合度高,程式碼越來越臃腫,團隊內部多人協作開發困難。
- Android專案在編譯程式碼的時候電腦會非常卡,又因為單一工程下程式碼耦合嚴重,每修改一處程式碼後都要重新編譯打包測試,導致非常耗時。
- 方便單元測試,改動單獨一個業務模組,不需要著重於關注其他模組被影響。
什麼是元件化
元件化就是將一個app分成多個Module,如下圖,每個Module都是一個元件(也可以是一個基礎庫供元件依賴),開發的過程中我們可以單獨除錯部分元件,元件間不需要互相依賴,但可以相互呼叫,最終釋出的時候所有元件以lib的形式被主app工程依賴並打包成一個apk。
元件化優勢
- 元件化就是將通用模組獨立出來,統一管理,以提高複用,將頁面拆分為粒度更小的元件,元件內部除了包含UI實現,還包含資料層和邏輯層。
- 每個工程都可以獨立編譯、加快編譯速度,獨立打包。
- 每個工程內部的修改,不會影響其他工程。
- 業務庫工程可以快速拆分出來,整合到其他App中。
- 迭代頻繁的業務模組採用元件方式,業務線研發可以互不干擾、提升協作效率,並控制產品質量,加強穩定性。
- 並行開發,團隊成員只關注自己的開發的小模組,降低耦合性,後期維護方便等。
指導思想
- 元件拆分:將一個project劃分成業務元件、基礎元件、路由元件。其中業務元件是相互隔離的,可以單獨除錯,基礎元件提供業務元件所公用的功能,路由元件為業務元件之間通訊提供支援。
- 元件隔離:業務元件之間的隔離,可以單獨除錯。
- 核心法則:編譯期隔離,執行期按需依賴。
依賴關係
元件化需要考慮的問題
- 模式切換:如何使得APP在單獨除錯跟整體除錯自由切換
- 資源衝突:當我們建立了多個Module的時候,如何解決相同資原始檔名合併的衝突
- 依賴關係:多個Module之間如何引用一些共同的library以及工具類
- 元件通訊:元件化之後,Module之間是相互隔離的,如何進行UI跳轉以及方法呼叫
- 入口引數:我們知道元件之間是有聯絡的,所以在單獨除錯的時候如何拿到其它的Module傳遞過來的引數
元件化後專案結構如下圖
理論說了那麼多,下面開始擼程式碼
實現步驟
1、全域性設定Gradle ,每一個業務Module需要的版本都定義在這裡方便後期維護多個Module版本號
ext {
// Sdk and tools
minSdkVersion = 16
targetSdkVersion = 26
compileSdkVersion = 26
buildToolsVersion = '26.0.2'
supportLibraryVersion = '26.1.0'
// App dependencies
aRouter = '1.2.2'
leakcanaryVersion = '1.3'
glideVersion = '3.7.0'
}
####每個業務Module編譯依賴版本
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
複製程式碼
2、模式切換
元件化後的每一個業務的module都可以是一個單獨的APP(isModuleRun=false), release 包的時候各個業務module作為lib依賴,這裡完全由一個變數控制,在根專案 gradle.properties裡面的 isModuleRun=true。
isModuleRun狀態不同,載入application和AndroidManifest都不一樣,以此來區分是獨立的APK還是lib,
實現方式如下
在build.grade
裡面配置
if (isModuleRun.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
### 單Module執行需要配置
sourceSets {
main {
if (isModuleRun.toBoolean()) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
//全部Module一起編譯的時候剔除debug目錄
exclude '**/debug/**'
}
}
}
}
複製程式碼
3、資源衝突
業務Module和BaseModule資原始檔名稱重複會產生衝突,解決方案在 每個 module 都有 app_name,為了不讓資源名重名,在每個元件的 build.gradle 中增加 resourcePrefix “xxx_強行檢查資源名稱字首。 固定每個元件的資源字首。但是 resourcePrefix 這個值只能限定 xml 裡面的資源,並不能限定圖片資源。 個人認為約定大於配置,團隊內協定好規範,可以避免衝突。
4、元件通訊 不熟悉ARouter基本用法的可以看看我的這篇文章 阿里巴巴ARouter基本使用方法
元件通訊框架在github上有star最多的有ARouter和ActivityArouter,前者是個人專案,後者是阿里巴巴開源,權衡之下選擇阿里的ARouter, 各業務Module之前不需要任何依賴可以通過路由跳轉,完美解決業務之間耦合 使用方式如下。
if (BuildConfig.DEBUG) { // 這兩行必須寫在init之前,否則這些配置在init過程中將無效
ARouter.openLog(); // 列印日誌
ARouter.openDebug(); // 開啟除錯模式(如果在InstantRun模式下執行,必須開啟除錯模式!線上版本需要關閉,否則有安全風險)
}
ARouter.init(this); // 儘可能早,推薦在Application中初始化
複製程式碼
compile "com.alibaba:arouter-api:$rootProject.aRouter"
每個業務Module都需要新增註解
annotationProcessor 'com.alibaba:arouter-compiler:1.1.3'
複製程式碼
跳轉方法 在目標Activity上新增path
@Route(path = ARouterManager.BModuleActivity)
public class BModuleActivity extends BaseActivity {
@Autowired
public String name;
@Autowired(name = "age")
int age;
TextView txt;
@Override
protected int getLayoutId() {
return R.layout.b_module_layout;
}
@Override
protected void initView() {
txt = findViewById(R.id.txt);
//String name = getIntent().getStringExtra("name"); 也可以這樣接受引數
ARouter.getInstance().inject(this);
txt.setText("name:" + name + ",age:" + age);
#開始跳轉
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 2\. 跳轉並攜帶引數
ARouter.getInstance().build(ARouterManager.BModuleActivity)
.withString("name", "888")
.withInt("age", 11)
.navigation();
}
});
複製程式碼
/**
* 路由管理類
*/
public final class ARouterManager {
public static final String AFragment = "/amodule/AFragment";
public static final String BFragment = "/bmodule/BFragment";
public static final String CFragment = "/cmodule/CFragment";
public static final String AModuleActivity = "/amodule/AAModuleActivity";
public static final String BModuleActivity = "/bmodule/BModuleActivity";
public static final String CModuleActivity = "/cmodule/CModuleActivity";
}
複製程式碼
上述只使用了ARouter的簡單用法,更多進階用法請參考ARouter文件, ARouter
5、Application
當元件單獨執行的時候,每個Module自成一個APK,那麼就意味著會有多個Application,很顯然我們不願意重複寫這麼多程式碼,所以我們只需要定義一個BaseApplication即可,其它的Application直接繼承此BaseApplication就OK了,BaseApplication裡面還可定義公用的引數。
連結:https://www.jianshu.com/p/010d946e8f67,轉載請註明原創
微信公眾號:終端研發部