組件化架構(gòu)思路

1、為什么要項(xiàng)目組件化

隨著 APP 版本不斷的迭代,新功能的不斷增加,業(yè)務(wù)也會變的越來越復(fù)雜,APP 業(yè)務(wù)模塊 的數(shù)量有可能還會繼續(xù)增加,而且每個(gè)模塊的代碼也變的越來越多,這樣發(fā)展下去單一工程 下的 APP 架構(gòu)勢必會影響開發(fā)效率,增加項(xiàng)目的維護(hù)成本,每個(gè)工程師都要熟悉如此之多 的代碼,將很難進(jìn)行多人協(xié)作開發(fā),而且 Android 項(xiàng)目在編譯代碼的時(shí)候電腦會非???,又 因?yàn)閱我还こ滔麓a耦合嚴(yán)重,每修改一處代碼后都要重新編譯打包測試,導(dǎo)致非常耗時(shí), 最重要的是這樣的代碼想要做單元測試根本無從下手,所以必須要有更靈活的架構(gòu)代替過去 單一的工程架構(gòu)。

  • 目前比較普遍使用的 Android APP 技術(shù)架構(gòu),往往是在一個(gè)界面中存在大量的業(yè)務(wù) 邏輯,而業(yè)務(wù)邏輯中充斥著各種網(wǎng)絡(luò)請求、數(shù)據(jù)操作等行為,整個(gè)項(xiàng)目中也沒有模塊的概念, 只有簡單的以業(yè)務(wù)邏輯劃分的文件夾,并且業(yè)務(wù)之間也是直接相互調(diào)用、高度耦合在一起的;
image.png
  • 業(yè)務(wù)包之間的依賴過于復(fù)雜


    image.png

    上圖單一工程模型下的業(yè)務(wù)關(guān)系,總的來說就是:你中有我,我中有你,相互依賴,無法分 離。然而隨著產(chǎn)品的迭代,業(yè)務(wù)越來越復(fù)雜,隨之帶來的是項(xiàng)目結(jié)構(gòu)復(fù)雜度的極度增加,此時(shí)我 們會面臨如下幾個(gè)問題:
    1.實(shí)際業(yè)務(wù)變化非???,但是單一工程的業(yè)務(wù)模塊耦合度太高,牽一發(fā)而動(dòng)全身;
    2.對工程所做的任何修改都必須要編譯整個(gè)工程;
    3.功能測試和系統(tǒng)測試每次都要進(jìn)行;
    4.團(tuán)隊(duì)協(xié)同開發(fā)存在較多的沖突.不得不花費(fèi)更多的時(shí)間去溝通和協(xié)調(diào),并且在開發(fā)過程中, 任何一位成員沒辦法專注于自己的功能點(diǎn),影響開發(fā)效率;
    5.不能靈活的對業(yè)務(wù)模塊進(jìn)行配置和組裝; 為了滿足各個(gè)業(yè)務(wù)模塊的迭代而彼此不受影響,更好的解決上面這種讓人頭疼的依賴關(guān)系, 就需要整改 App 的架構(gòu)。

2、如何組件化

  • 組件化工程模型


    image.png

    上圖是組件化工程模型,為了方便理解這張架構(gòu)圖,下面會列舉一些組件化工程中用到的名 詞的含義:

名詞 含義
集成模式 所有的業(yè)務(wù)組件被“app 殼工程”依賴,組成一個(gè)完整的 APP
組件模式 可以獨(dú)立開發(fā)業(yè)務(wù)組件,每一個(gè)業(yè)務(wù)組件就是一個(gè) APP
app 殼工程 負(fù)責(zé)管理各個(gè)業(yè)務(wù)組件,和打包 apk,沒有具體的業(yè)務(wù)功能
業(yè)務(wù)組件 根據(jù)公司具體業(yè)務(wù)而獨(dú)立形成一個(gè)的工程
功能組件 提供開發(fā) APP 的某些基礎(chǔ)功能,例如多媒體文件播放等
Main 組件 屬于業(yè)務(wù)組件,指定 APP 啟動(dòng)頁面、主界面
Common組件 屬于功能組件,支撐業(yè)務(wù)組件的基礎(chǔ),提供多數(shù)業(yè)務(wù)組件需要的功能,例 如提供網(wǎng)絡(luò)請求功能,打印日志,統(tǒng)一管理第三方依賴庫
  • 組件化工程依賴關(guān)系
image.png

Android APP 組件化架構(gòu)的目標(biāo)是告別結(jié)構(gòu)臃腫,讓各個(gè)業(yè)務(wù)變得相對獨(dú)立,業(yè)務(wù)組件在組件模式下可以獨(dú)立開發(fā),而在集成模式下又可以變?yōu)?arr 包集成到“app 殼工程”中,組成一 個(gè)完整功能的 APP; 從組件化工程模型中可以看到,業(yè)務(wù)組件之間是獨(dú)立的,沒有關(guān)聯(lián)的,這些業(yè)務(wù)組件在集 成模式下是一個(gè)個(gè) library,被 app 殼工程所依賴,組成一個(gè)具有完整業(yè)務(wù)功能的 APP 應(yīng)用, 但是在組件開發(fā)模式下,業(yè)務(wù)組件又變成了一個(gè)個(gè) application,它們可以獨(dú)立開發(fā)和調(diào)試, 由于在組件開發(fā)模式下,業(yè)務(wù)組件們的代碼量相比于完整的項(xiàng)目差了很遠(yuǎn),因此在運(yùn)行時(shí)可 以顯著減少編譯時(shí)間。

  • 組件化帶來的好處
    這是組件化工程模型下的業(yè)務(wù)關(guān)系,業(yè)務(wù)之間將不再直接引用和依賴,而是通過“路由”這樣 一個(gè)中轉(zhuǎn)站間接產(chǎn)生聯(lián)系,而 Android 中的路由實(shí)際就是對 URL Scheme 的封裝; 如此規(guī)模大的架構(gòu)整改需要付出更高的成本,還會涉及一些潛在的風(fēng)險(xiǎn),但是整改后的架構(gòu) 能夠帶來很多好處:
    1、加快業(yè)務(wù)迭代速度,各個(gè)業(yè)務(wù)模塊組件更加獨(dú)立,不再出現(xiàn)業(yè)務(wù)耦合情況;
    2、穩(wěn)定的公共模塊采用依賴庫方式,提供給各個(gè)業(yè)務(wù)線使用,減少重復(fù)開發(fā)和維護(hù)工作量;
    3、迭代頻繁的業(yè)務(wù)模塊采用組件方式,各業(yè)務(wù)研發(fā)可以互不干擾、提升協(xié)作效率,并控制 產(chǎn)品質(zhì)量;
    4、為新業(yè)務(wù)隨時(shí)集成提供了基礎(chǔ),所有業(yè)務(wù)可上可下,靈活多變;
    5、降低團(tuán)隊(duì)成員熟悉項(xiàng)目的成本,降低項(xiàng)目的維護(hù)難度;
    6、加快編譯速度,提高開發(fā)效率;
    7、控制代碼權(quán)限,將代碼的權(quán)限細(xì)分到更小的粒度;

3、組件化實(shí)施流程

1)組件模式和集成模式的轉(zhuǎn)換

Android Studio 中的 Module 主要有兩種屬性,分別為:
1、application 屬性,可以獨(dú)立運(yùn)行的 Android 程序,也就是我們的 APP; apply plugin: ‘com.android.application’
2、library 屬性,不可以獨(dú)立運(yùn)行,一般是 Android 程序依賴的庫文件; apply plugin: ‘com.android.library’

Module 的屬性是在每個(gè)組件的 build.gradle 文件中配置的,當(dāng)我們在組件模式開發(fā)時(shí),業(yè) 務(wù)組件應(yīng)處于 application 屬性,這時(shí)的業(yè)務(wù)組件就是一個(gè) Android App,可以獨(dú)立開發(fā)和 調(diào)試;而當(dāng)我們轉(zhuǎn)換到集成模式開發(fā)時(shí),業(yè)務(wù)組件應(yīng)該處于 library 屬性,這樣才能被我們 的“app 殼工程”所依賴,組成一個(gè)具有完整功能的 APP; 但是我們?nèi)绾巫尳M件在這兩種模式之間自動(dòng)轉(zhuǎn)換呢?總不能每次需要轉(zhuǎn)換模式的時(shí)候去每 個(gè)業(yè)務(wù)組件的 Gralde 文件中去手動(dòng)把 Application 改成 library 吧?如果我們的項(xiàng)目只 有兩三個(gè)組件那么這個(gè)辦法肯定是可行的,手動(dòng)去改一遍也用不了多久,但是在大型項(xiàng)目中 我們可能會有十幾個(gè)業(yè)務(wù)組件,再去手動(dòng)改一遍必定費(fèi)時(shí)費(fèi)力,這時(shí)候就需要程序員發(fā)揮下 懶的本質(zhì)了。 試想,我們經(jīng)常在寫代碼的時(shí)候定義靜態(tài)常量,那么定義靜態(tài)常量的目的什么呢?當(dāng)一個(gè)常 量需要被好幾處代碼引用的時(shí)候,把這個(gè)常量定義為靜態(tài)常量的好處是當(dāng)這個(gè)常量的值需要 改變時(shí)我們只需要改變靜態(tài)常量的值,其他引用了這個(gè)靜態(tài)常量的地方都會被改變,做到了 一次改變,到處生效;根據(jù)這個(gè)思想,那么我們就可以在我們的代碼中的某處定義一個(gè)決定 業(yè)務(wù)組件屬性的常量,然后讓所有業(yè)務(wù)組件的 build.gradle 都引用這個(gè)常量,這樣當(dāng)我們改 變了常量值的時(shí)候,所有引用了這個(gè)常量值的業(yè)務(wù)組件就會根據(jù)值的變化改變自己的屬性; 可是問題來了?靜態(tài)常量是用 Java 代碼定義的,而改變組件屬性是需要在 Gradle 中定義 的,Gradle 能做到嗎? Gradle 自動(dòng)構(gòu)建工具有一個(gè)重要屬性,可以幫助我們完成這個(gè)事情。每當(dāng)我們用 AndroidStudio 創(chuàng)建一個(gè) Android 項(xiàng)目后,就會在項(xiàng)目的根目錄中生成一個(gè)文 件 gradle.properties,我們將使用這個(gè)文件的一個(gè)重要屬性:在 Android 項(xiàng)目中的任何一 個(gè) build.gradle 文件中都可以把 gradle.properties 中的常量讀取出來;那么我們在上面提 到解決辦法就有了實(shí)際行動(dòng)的方法,首先我們在 gradle.properties 中定義一個(gè)常量 值 isModule(是否是組件開發(fā)模式,true 為是,false 為否): # 每次更改“isModule”的值后,需要點(diǎn)擊 "Sync Project" 按鈕 isModule=false 然后我們在業(yè)務(wù)組件的 build.gradle 中讀取 isModule,但是 gradle.properties 還有一個(gè)重 要屬性: gradle.properties 中的數(shù)據(jù)類型都是 String 類型,使用其他數(shù)據(jù)類型需要自行 轉(zhuǎn)換;也就是說我們讀到 isModule 是個(gè) String 類型的值,而我們需要的是 Boolean 值, 代碼如下:

 if (isModule.toBoolean()) {
 apply plugin: 'com.android.application'
} else { 
apply plugin: 'com.android.library' 
}

這樣我們第一個(gè)問題就解決了,當(dāng)然了 每次改變 isModule 的值后,都要同步項(xiàng)目才能生 效;

2)組件之間 AndroidManifest 合并問題

在 AndroidStudio 中每一個(gè)組件都會有對應(yīng)的 AndroidManifest.xml,用于聲明需要的權(quán) 限、Application、Activity、Service、Broadcast 等,當(dāng)項(xiàng)目處于組件模式時(shí),業(yè)務(wù)組件的 AndroidManifest.xml 應(yīng)該具有一個(gè) Android APP 所具有的的所有屬性,尤其是聲明 Application 和要 launch 的 Activity,但是當(dāng)項(xiàng)目處于集成模式的時(shí)候,每一個(gè)業(yè)務(wù)組件的 AndroidManifest.xml 都要合并到“app 殼工程”中,要是每一個(gè)業(yè)務(wù)組件都有自己的 Application 和 launch 的 Activity,那么合并的時(shí)候肯定會沖突,試想一個(gè) APP 怎么可能會 有多個(gè) Application 和 launch 的 Activity 呢? 但是大家應(yīng)該注意到這個(gè)問題是在組件開發(fā)模式和集成開發(fā)模式之間轉(zhuǎn)換引起的問題,而在 上一節(jié)中我們已經(jīng)解決了組件模式和集成模式轉(zhuǎn)換的問題,另外大家應(yīng)該都經(jīng)歷過將 Android 項(xiàng)目從 Eclipse 切換到 AndroidStudio 的過程,由于 Android 項(xiàng)目在 Eclipse 和 AndroidStudio 開發(fā)時(shí) AndroidManifest.xml 文件的位置是不一樣的,我們需要在 build.gradle 中指定下 AndroidManifest.xml 的位置,AndroidStudio 才能讀取到 AndroidManifest.xml,這樣解決辦法也就有了,我們可以為組件開發(fā)模式下的業(yè)務(wù)組件再 創(chuàng)建一個(gè) AndroidManifest.xml,然后根據(jù) isModule 指定 AndroidManifest.xml 的文件 路徑,讓業(yè)務(wù)組件在集成模式和組件模式下使用不同的 AndroidManifest.xml,這樣表單 沖突的問題就可以規(guī)避了


image.png

上圖是組件化項(xiàng)目中一個(gè)標(biāo)準(zhǔn)的業(yè)務(wù)組件目錄結(jié)構(gòu),首先我們在 main 文件夾下創(chuàng)建一個(gè) module 文件夾用于存放組件開發(fā)模式下業(yè)務(wù)組件的 AndroidManifest.xml,而AndroidStudio 生成的 AndroidManifest.xml 則依然保留,并用于集成開發(fā)模式下業(yè)務(wù)組件 的表單;然后我們需要在業(yè)務(wù)組件的 build.gradle 中指定表單的路徑,代碼如下:

    sourceSets {
        main {
            if (isDebugModel.toBoolean()) {
                manifest.srcFile 'src/main/java/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }

這樣在不同的開發(fā)模式下就會讀取到不同的 AndroidManifest.xml ,然后我們需要修改這 兩個(gè)表單的內(nèi)容以為我們不同的開發(fā)模式服務(wù)。

3)全局 Context 的獲取及組件數(shù)據(jù)初始化

在Common組件中創(chuàng)建BaseApplication 然后在業(yè)務(wù)組件中debug文件夾下集成BaseApplication 在debug模式下的AndroidManifest.xml中聲明DebugApplication. 這樣在開發(fā)者模式下就可以有自己的DebugApplication了,同時(shí)可以在里面初始化一些自己本模塊需要的原始數(shù)據(jù).而BaseApplication提供了getContext()方法且在Common模塊里面不會因?yàn)槟J降那袚Q導(dǎo)致代碼無法訪問.

    sourceSets {
        main {
            if (isDebugModel.toBoolean()) {
                manifest.srcFile 'src/main/java/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //集成開發(fā)模式下排除 debug 文件夾中的所有 Java 文件
                java { exclude 'debug/**' }
            }
        }
    }
image.png

4)library 依賴問題

  • 第三方包和我們自己的包存在重復(fù)加載導(dǎo)致編譯不過
    解決辦法:根據(jù)組件名排除或者根據(jù)包名排除
    下面以排除 support-v4 庫為例:
dependencies { 
implementation fileTree(dir: 'libs', include: ['*.jar']) 
implementation ("com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion")
 { 
      exclude module: 'support-v4'//根據(jù)組件名排除
      exclude group: 'android.support.v4'//根據(jù)包名排除 
  } 
}
  • 第三方依賴庫統(tǒng)一管理
    前面介紹的 Common 庫的作用之一就是統(tǒng)一依賴開源庫,因?yàn)槠渌麡I(yè)務(wù)組件都依賴 了 Common 庫,所以這些業(yè)務(wù)組件也就間接依賴了 Common 所依賴的開源庫。
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    testImplementation deps.junit
    androidTestImplementation deps.runner
    androidTestImplementation deps.espresso_core

    implementation deps.appcompat
    //網(wǎng)絡(luò)
    api deps.lifecycle
    api deps.okhttp
    api deps.logging_interceptor
    api deps.retrofit
    api deps.adapter_rxjava2
    api deps.converter_gson
    api deps.rxandroid
    api deps.rxjava
    api deps.rxlifecycle
}

5)組件之間調(diào)用和通信

在組件化開發(fā)的時(shí)候,組件之間是沒有依賴關(guān)系,我們不能在使用顯示調(diào)用來跳轉(zhuǎn)頁面了, 因?yàn)槲覀兘M件化的目的之一就是解決模塊間的強(qiáng)依賴問題,假如現(xiàn)在要從 A 業(yè)務(wù)組件跳轉(zhuǎn) 到業(yè)務(wù) B 組件,并且要攜帶參數(shù)跳轉(zhuǎn),這時(shí)候怎么辦呢?而且組件這么多怎么管理也是個(gè) 問題,這時(shí)候就需要引入“路由”的概念了,由本文開始的組件化模型下的業(yè)務(wù)關(guān)系圖可知路 由就是起到一個(gè)轉(zhuǎn)發(fā)的作用。
這里我推薦使用ARouter詳細(xì)使用就不贅述了,參考如下兩個(gè)鏈接
https://mp.weixin.qq.com/s/ao__wU3tNS2y6dh-wULb-A
https://github.com/alibaba/ARouter/blob/master/README_CN.md

6)組件之間資源名沖突

建議模塊創(chuàng)建時(shí)里面的資源文件統(tǒng)一前綴命名規(guī)則.

4、組件化項(xiàng)目的工程類型

1)app 殼工程

app 殼工程是從名稱來解釋就是一個(gè)空殼工程,沒有任何的業(yè)務(wù)代碼,也不能有 Activity, 但它又必須被單獨(dú)劃分成一個(gè)組件,而不能融合到其他組件中,是因?yàn)樗腥缦聨c(diǎn)重要功 能:

  1. app 殼工程中聲明了我們 Android 應(yīng)用的 Application,這個(gè) Application 必須繼承自 Common 組件中的 BaseApplication(如果你無需實(shí)現(xiàn)自己的 Application 可以直接在表單 聲明 BaseApplication),因?yàn)橹挥羞@樣,在打包應(yīng)用后才能讓 BaseApplication 中的 Context 生效,當(dāng)然你還可以在這個(gè) Application 中初始化我們工程中使用到的庫文件,還可以在這 里解決 Android 引用方法數(shù)不能超過 65535 的限制,對崩潰事件的捕獲和發(fā)送也可以在這 里聲明。

  2. app 殼工程的 AndroidManifest.xml 是我 Android 應(yīng)用的根表單,應(yīng)用的名稱、圖標(biāo) 以及是否支持備份等等屬性都是在這份表單中配置的,其他組件中的表單最終在集成開發(fā)模 式下都被合并到這份 AndroidManifest.xml 中。

  3. app 殼工程的 build.gradle 是比較特殊的,app 殼不管是在集成開發(fā)模式還是組件開 發(fā)模式,它的屬性始終都是:com.android.application,因?yàn)樽罱K其他的組件都要被 app 殼 工程所依賴,被打包進(jìn) app 殼工程中,這一點(diǎn)從組件化工程模型圖中就能體現(xiàn)出來,所以 app 殼工程是不需要單獨(dú)調(diào)試單獨(dú)開發(fā)的。另外 Android 應(yīng)用的打包簽名,以及 buildTypes 和 defaultConfig 都需要在這里配置,而它的 dependencies 則需要根據(jù) isModule 的值分別 依賴不同的組件,在組件開發(fā)模式下 app 殼工程只需要依賴 Common 組件,或者為了防止 報(bào)錯(cuò)也可以根據(jù)實(shí)際情況依賴其他功能組件,而在集成模式下 app 殼工程必須依賴所有在 應(yīng)用 Application 中聲明的業(yè)務(wù)組件,并且不需要再依賴任何功能組件。

2)功能組件和 Common 組件

  • 功能組件是為了支撐業(yè)務(wù)組件的某些功能而獨(dú)立劃分出來的組件,功能實(shí)質(zhì)上跟項(xiàng)目中引入 的第三方庫是一樣的,功能組件的特征如下:
  1. 功能組件的 AndroidManifest.xml 是一張空表,這張表中只有功能組件的包名;
  2. 功能組件不管是在集成開發(fā)模式下還是組件開發(fā)模式下屬性始終是: com.android.library,所以功能組件是不需要讀取 gradle.properties 中的 isModule 值的; 另外功能組件的 build.gradle 也無需設(shè)置 buildTypes ,只需要 dependencies 這個(gè)功能 組件需要的 jar 包和開源庫。
  • Common 組件除了有功能組件的普遍屬性外,還具有其他功能:
  1. Common 組件的 AndroidManifest.xml 不是一張空表,這張表中聲明了我們 Android 應(yīng)用用到的所有使用權(quán)限 uses-permission 和 uses-feature,放到這里是因?yàn)樵诮M件開發(fā) 模式下,所有業(yè)務(wù)組件就無需在自己的 AndroidManifest.xm 聲明自己要用到的權(quán)限了。
  2. Common 組件的 build.gradle 需要統(tǒng)一依賴業(yè)務(wù)組件中用到的 第三方依賴庫和 jar 包, 例如我們用到的 ARouter、Okhttp 等等。
  3. Common 組件中封裝了 Android 應(yīng)用的 Base 類和網(wǎng)絡(luò)請求工具、圖片加載工具等等, 公用的 widget 控件也應(yīng)該放在 Common 組件中;業(yè)務(wù)組件中都用到的數(shù)據(jù)也應(yīng)放于 Common 組件中,例如保存到 SharedPreferences 和 DataBase 中的登陸數(shù)據(jù);
  4. Common 組件的資源文件中需要放置項(xiàng)目公用的 Drawable、layout、sting、dimen、 color 和 style 等等,另外項(xiàng)目中的 Activity 主題必須定義在 Common 中,方便和 BaseActivity 配合保持整個(gè) Android 應(yīng)用的界面風(fēng)格統(tǒng)一。

3)業(yè)務(wù)組件和 Main 組件

  • 業(yè)務(wù)組件就是根據(jù)業(yè)務(wù)邏輯的不同拆分出來的組件,業(yè)務(wù)組件的特征如下:
  1. 業(yè)務(wù)組件中要有兩張 AndroidManifest.xml,分別對應(yīng)組件開發(fā)模式和集成開發(fā)模式.
  2. 業(yè)務(wù)組件在集成模式下是不能有自己的 Application 的,但在組件開發(fā)模式下又必須實(shí)現(xiàn) 自己的 Application 并且要繼承自 Common 組件的 BaseApplication,并且這個(gè) Application 不能被業(yè)務(wù)組件中的代碼引用,因?yàn)樗墓δ芫褪菫榱耸箻I(yè)務(wù)組件從 BaseApplication 中獲 取的全局 Context 生效,還有初始化數(shù)據(jù)之用。
  3. 業(yè)務(wù)組件有 debug 文件夾,這個(gè)文件夾在集成模式下會從業(yè)務(wù)組件的代碼中排除掉,所 以 debug 文件夾中的類不能被業(yè)務(wù)組件強(qiáng)引用,例如組件模式下的 Application 就是置于 這個(gè)文件夾中,還有組件模式下開發(fā)給目標(biāo) Activity 傳遞參數(shù)的用的 launch Activity 也應(yīng) 該置于 debug 文件夾中;
  4. 業(yè)務(wù)組件必須在自己的 build.gradle 中根據(jù) isModule 值的不同改變自己的屬性,在 組件模式下是:com.android.application,而在集成模式下 com.android.library;同時(shí)還需 要在 build.gradle 配置資源文件,如 指定不同開發(fā)模式下的 AndroidManifest.xml 文件路徑, 排除 debug 文件夾等;業(yè)務(wù)組件還必須在 dependencies 中依賴 Common 組件,并且引入 ActivityRouter 的注解處理器 annotationProcessor,以及依賴其他用到的功能組件。

下面是一份普通業(yè)務(wù)組件的 build.gradle 文件:

if (isModule.toBoolean()) {
 apply plugin: 'com.android.application' 
} else { 
apply plugin: 'com.android.library' 
}
...
sourceSets { 
main {
     if (isModule.toBoolean()) {
     manifest.srcFile 'src/main/module/AndroidManifest.xml' 
      } else { 
      manifest.srcFile 'src/main/AndroidManifest.xml' //集成開發(fā)模式下排除 debug 文件夾中的所有 Java 文件 java 
    {
       exclude 'debug/**' 
    } 
    }
   } 
}
...
  • Main 組件除了有業(yè)務(wù)組件的普遍屬性外,還有一項(xiàng)重要功能:
    1、Main 組件集成模式下的 AndroidManifest.xml 是跟其他業(yè)務(wù)組件不一樣的,Main 組件的 表單中聲明了我們整個(gè) Android 應(yīng)用的 launch Activity,這就是 Main 組件的獨(dú)特之處;所 以我建議 SplashActivity、登陸 Activity 以及主界面都應(yīng)屬于 Main 組件,也就是說 Android 應(yīng)用啟動(dòng)后要調(diào)用的頁面應(yīng)置于 Main 組件。

5、組件化項(xiàng)目的混淆方案

組件化項(xiàng)目的 Java 代碼混淆方案采用在集成模式下集中在 app 殼工程中混淆,各個(gè)業(yè)務(wù) 組件不配置混淆文件。集成開發(fā)模式下在 app 殼工程中 build.gradle 文件的 release 構(gòu)建類 型中開啟混淆屬性,其他 buildTypes 配置方案跟普通項(xiàng)目保持一致,Java 混淆配置文件也 放置在 app 殼工程中,各個(gè)業(yè)務(wù)組件的混淆配置規(guī)則都應(yīng)該在 app 殼工程中的混淆配置文 件中添加和修改。 之所以不采用在每個(gè)業(yè)務(wù)組件中開啟混淆的方案,是因?yàn)?組件在集成模式下都被 Gradle 構(gòu)建成了 release 類型的 arr 包,一旦業(yè)務(wù)組件的代碼被混淆,而這時(shí)候代碼中又出現(xiàn)了 bug,將很難根據(jù)日志找出導(dǎo)致 bug 的原因;另外每個(gè)業(yè)務(wù)組件中都保留一份混淆配置文件 非常不便于修改和管理,這也是不推薦在業(yè)務(wù)組件的 build.gradle 文件中配置 buildTypes (構(gòu)建類型)的原因。

6、工程的 build.gradle 和 gradle.properties 文件

1)組件化工程的 build.gradle 文件

在組件化項(xiàng)目中因?yàn)槊總€(gè)組件的 build.gradle 都需要配置 compileSdkVersion、 buildToolsVersion 和 defaultConfig 等的版本號,而且每個(gè)組件都需要用到 annotationProcessor,為了能夠使組件化項(xiàng)目中的所有組件的 build.gradle 中的這些配置 都能保持統(tǒng)一,并且也是為了方便修改版本號,我們統(tǒng)一在 Android 工程根目錄下的 build.gradle 中定義這些版本號,當(dāng)然為了方便管理 Common 組件中的第三方開源庫的版本 號,最好也在這里定義這些開源庫的版本號,然后在各個(gè)組件的 build.gradle 中引用 Android 工程根目錄下的 build.gradle 定義的版本號,組件化工程的 build.gradle 文件代碼如下:

...
ext {
// Sdk and tools 
//localBuildToolsVersion 是 gradle.properties 中的數(shù)據(jù) buildToolsVersion = localBuildToolsVersion compileSdkVersion = 25 
minSdkVersion = 16 
targetSdkVersion = 25 
versionCode = 1 
versionName = "1.0" 
javaVersion = JavaVersion.VERSION_1_8 // App dependencies version 
supportLibraryVersion = "25.3.1" 
retrofitVersion = "2.1.0" 
glideVersion = "3.7.0" 
loggerVersion = "1.15" 
eventbusVersion = "3.0.0" 
gsonVersion = "2.8.0" 
photoViewVersion = "2.0.0" //需檢查升級版本 annotationProcessor = "1.1.7"
routerVersion = "1.2.2" 
easyRecyclerVersion = "4.4.0" 
cookieVersion = "v1.0.1" 
toastyVersion = "1.1.3"
...

2)組件化工程的 gradle.properties 文件

  1. 首先在Android工程的 gradle.properties 中分別定義兩個(gè)常量:localBuildToolsVersion 和 localGradlePluginVersion,分別表示 buildToolsVersion 和 Gradle Build Tools 的版本號:
# 為自動(dòng)化出包配置(因?yàn)槊總€(gè)開發(fā)的電腦壞境不一致)
localBuildToolsVersion=25.0.3
# 這個(gè)值一般跟你的AndroidStudio版本號一致
localGradlePluginVersion=2.3.2
  1. 然后在Android工程的 build.gradle 中引用 localGradlePluginVersion:
 dependencies {
        //classpath "com.android.tools.build:gradle:$localGradlePluginVersion"
        //$localGradlePluginVersion是gradle.properties中的數(shù)據(jù)
        classpath "com.android.tools.build:gradle:$localGradlePluginVersion"
    }
  1. 在組件的build.gradle中引用 localBuildToolsVersion:
android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    //localBuildToolsVersion是gradle.properties中的數(shù)據(jù)
    buildToolsVersion localBuildToolsVersion

    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
    }
}
  1. 最后是最重要的一步,一定要將 gradle.properties 從版本控制工具(Git、SVN)中給忽略掉,千萬不要把這個(gè)文件提交到代碼倉庫;然后把配置好的 gradle.properties 給每個(gè)開發(fā)人員發(fā)一份,供他們本地使用,至于 gradle.properties 中的版本號隨便他們改好了,反正又不會傳到代碼倉庫。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容