天天看點

Android篇丨震坤行移動端元件化演進之路

作者:閃念基因

随着移動項目的不斷發展,移動端軟體也變得越來越複雜,體積也變得越來越龐大;為了降低軟體的複雜度和耦合度,同時也是為了子產品重用、團隊開發效率等原因,移動端元件化成了衆多公司需要探索和實踐的技術方向。本文主要介紹震坤行移動端元件化如何實施,希望對從事移動端元件化的同學有所啟發。

Android篇丨震坤行移動端元件化演進之路
Android篇丨震坤行移動端元件化演進之路

1

演進之路背景

1.1 什麼是元件化

元件化就是由多個獨立的子子產品組合成一個整體,降低子產品間的耦合,這些子子產品可以随意拼裝組合, 而且子子產品可以獨立運作。

1.2 元件化的原因

震坤行App之前的架構的單一工程模式,随着App的快速疊代,帶來新功能的不斷增加,這種架構存在以下一些問題:

Android篇丨震坤行移動端元件化演進之路

◆ 業務之間代碼耦合太重,稍微改動一個地方可能會導緻多個業務子產品受到影響,對開發和維護帶來很大的成本;

◆ 随着業務增⻓帶來團隊規模擴大,這時就需要注意多人協作開發問題。元件化可以使各業務子產品之間互相隔離,支援獨立開發、編譯、測試;

◆ 單一工程模式下随着代碼量的增加會導緻編譯代碼非常慢,影響開發效率;

◆ 公司後續會衍生出一些獨立應用,如物流助手等,希望能夠直接複用之前代碼進行快速開發。

1.3 元件化的目标

本次元件化架構調整的預期目标如下:

1. 提高元件複用性,節省開發和維護成本;

2. 降低業務子產品的耦合,業務移植更簡單;

3. 業務子產品可以單獨開發、運作,由各業務線獨立負責;

4. 業務子產品可以單獨編譯打包,加快編譯速度;

5. 元件之間可以靈活組建,快速生成其他應用;

6. 多個App共用元件,可以保證整個技術方案的統一性。

2

震坤行元件化實踐

2.1 架構設計圖

Android篇丨震坤行移動端元件化演進之路

宿主層:空殼app,包含一些全局配置和主Activity,不包含任何業務代碼。

業務層:包含各業務元件,互相之間依賴隔離,通過路由總線通信。

平台層:包含基礎業務SDK和業務無關的通用代碼,供上層各業務元件調用。

通過對各個子產品進行解耦,上層子產品對下層子產品單向依賴,不存在跨層級依賴和同層級依賴。業務元件之間通過路由總線和消息總線進行通信。

元件化過程中主要面對的問題包括如下幾個:

◆ 元件如何快速抽離;

◆ 元件之間互相隔離,如何進行UI跳轉和通信;

◆ 元件如何單獨運作。

2.2 如何建立元件

在Android開發中借助Gradle可以很友善的進行代碼拆分。在Gradle多子產品工程中有一個根目錄,每個子產品有一個子目錄。為了讓Gradle知道工程的結構以及每個子目錄是什麼子產品,需要在根目錄中添加一個settings.gradle檔案,每個子產品提供自己的build.gradle檔案。

Android篇丨震坤行移動端元件化演進之路

settings.gradle 檔案聲明了工程的所有子產品:

Android篇丨震坤行移動端元件化演進之路

如果要把library子產品作為依賴被app子產品包含的話,隻需要在app子產品的build.gradle檔案添加如下代碼:

Android篇丨震坤行移動端元件化演進之路

1.元件抽離步驟

1. 先抽離common_component,主要封裝了項目中需要的基礎功能,如Utils包,網絡庫,資料庫部分。所有業務元件都會依賴common_component;

2. 抽離最簡單的業務元件,先代碼檔案然後資源檔案。針對所依賴的檔案如果沒有别的業務元件用到則移動到該業務Module,否則移動到common_component中;

3. 按照2步驟繼續抽離其他元件,在抽離的過程中common_component會越來越大,後續我們會對其做精簡;

4. 添加路由架構,完善元件間的跳轉和通信;

5. 精簡common_component,将裡面的内容反向抽離到業務元件。比如Utils包裡面的檔案進行分離,不同的Utils類、方法放到對應的業務元件中;Retrofit裡面将相同的業務接口放到同一個API接口中,然後抽離到業務元件中。逐漸将common_component精簡到不包含不需要的業務代碼。

2. AndroidManifest合并

在單一工程結構中,APP運作需要的四大元件和權限注冊都放到同一個AndroidManifest檔案中。而當每個Module包含自己的AndroidManifest檔案,當最終生成App的時候會将多個AndroidManifest合成一個,即最終會将各個AndroidManifest中注冊的四大元件合并到一起,合成的位址目錄在:

Android篇丨震坤行移動端元件化演進之路

1. 如果在一個功能Module中聲明所需要的權限,那麼在主Module中就會看到相應的權限;

2. 如果在其他module中都聲明相同的權限,最終的AndroidManifest會合并這個重複聲明的權限,是以相同的權限隻會聲明一次。

是以我們将各個業務所需要的四大元件在各自Module的AndroidManifest中聲明,一些基礎通用的權限在common_component中聲明,業務Module單獨需要的權限(如錄音權限)在自己的AndroidManifest中聲明。

3.全局變量設定

Android系統會為每個程式建立一個Application類的對象,Application對象的生命周期是整個程式中最⻓的,它的生命周期就等于這個程式的生命周期。我們經常會在使用Context的地方使用Application,但是元件化項目中Application定義在殼App中,各業務Module無法通路。

在Android開發中我們會在build.gradle中不同環境配置不同的buildConfigField值,系統編譯後會自動生成BuildConfig類,該類包含我們配置的所有buildConfigField值,但是業務元件獲得BuildConfig的不是主App的,而且自己Library Project生成的BuildConfig,裡面沒有主App中設定的值。

Android篇丨震坤行移動端元件化演進之路

在common_component中定義一個Application基類,各業務元件和殼App中的Application都依賴于 它,業務元件通過該基類獲得全局Context。同樣我們需要将 BuildConfig 的值下方到common_component中供業務元件使用,最終設定如下:

common_component/ZApplication

Android篇丨震坤行移動端元件化演進之路
Android篇丨震坤行移動端元件化演進之路

app/ZKHApplication

Android篇丨震坤行移動端元件化演進之路

4.元件間資源沖突

在元件化過程中,資源檔案如color、shape、drawable、layout等都有可能造成資源名沖突,因為不同 子產品的開發是分開的,如果項目組沒有按照統一規範命名就會出現資源名沖突。我們可以通過resourcePrefix 來避免,設定了該值後該元件中所有的資源名必須以指定的字元串為字首,否則會報錯。但是 resourcePrefix 隻能限定xml中的資源,并不能限定圖檔資源,是以我們也需要手動将圖檔資源名添加 resourcePrefix。

2.3 統一配置檔案

由于每個Module都有自己的build.gradle檔案,該檔案裡面包含工程所需要的各種配置資訊,為了統一 各Module版本依賴資訊, 以及讓後續維護子產品配置更簡單,我們第一個需要解決的問題就是要多Module之間統一配置檔案。目前Android項目管理Gradle依賴主要由下面三種方法:

1. 手動管理;

2. ext方式(Google推薦);

3. kotlin + buildSrc

我們項目中采用第三種方法,該方法與ext方式類似,不過在它基礎上增加了IDE自動補全和單擊跳轉。

1.手動管理

這是最基礎的管理依賴方法,每次新增子產品都要從原來子產品中将配置和依賴資訊複制一份以保持一緻;而且每次更新依賴的時候都需要大量的手動更改。

module_a/build.gradle

Android篇丨震坤行移動端元件化演進之路

module_b/build.gradle

Android篇丨震坤行移動端元件化演進之路

2.ext方式

Google推薦的依賴管理方法,該方法将所有配置和依賴放在一起統一管理,各個Library Project通過ext來通路

Root-level build.gradle

Android篇丨震坤行移動端元件化演進之路

module_a/build.gradle

Android篇丨震坤行移動端元件化演進之路

module_b/build.gradle

Android篇丨震坤行移動端元件化演進之路

3.kotlin + buildSrc

該方法在ext方式上更新提供了IDE自動補全和點選跳轉功能。具體實作需要在項目中建立一個buildSrc子產品,然後編寫kotlin代碼來管理依賴庫。

Android篇丨震坤行移動端元件化演進之路

kotlin + buildScr實作

Android篇丨震坤行移動端元件化演進之路

1. build.gradle.kts

Android篇丨震坤行移動端元件化演進之路

2. Dependencis.kt(kotlin代碼)

Android篇丨震坤行移動端元件化演進之路

2.4 元件間通信

将工程進行元件隔離之後,需要解決的問題就是元件之間的通信。這個主要由路由和消息總線來實作,其中路由架構主要用于UI跳轉群組件之間一對一通信,而事件總線用來多對多通信。

1.路由架構的好處

1. Android⻚面跳轉中顯示Intent會導緻耦合太大,不适合元件化拆分;隐式Inten使用起來太麻煩,隐式Intent多了之後會讓開發人員無從下手,而且會導緻AndroidManifest檔案增大;

2. 支援動态修改路由,支援全局或者局部降級政策,再用不用擔心由于Activity找不到而發生崩潰問題;

3. 支援跳轉攔截,可以統一處理登入,埋點等邏輯。如在未登入情況下,打開登陸⻚面,登入成功後打開剛才想打開的⻚面;

4. 支援标準URL跳轉,可以使Android、iOS、H5統一跳轉路徑。服務端可以在資料層面控制用戶端的跳轉邏輯;

5. 支援跨子產品API調用,通過控制反轉來做元件解耦。

Android篇丨震坤行移動端元件化演進之路

2.路由架構ARouter

ARouter是阿裡推出的路由引擎,是一個路由架構,并不是完整的元件化方案,可作為元件化架構的通信引擎。它主要機制是路由+接口下沉:

路由:編譯期生成路由表,用來實作UI跳轉。

接口下沉:接口內建IProvider并下沉到commmon_component中,元件中實作接口并通過注解暴露服務。

基礎功能:1. 添加依賴和配置

Android篇丨震坤行移動端元件化演進之路

如果是Kotlin項目則配置如下:

Android篇丨震坤行移動端元件化演進之路

基礎功能:2. 添加注解

Android篇丨震坤行移動端元件化演進之路

基礎功能:3.初始化SDK

Android篇丨震坤行移動端元件化演進之路

基礎功能:4. 發起路由操作

Android篇丨震坤行移動端元件化演進之路

3.項目中ARouter使用

在common_component中定義各個業務元件的IProvider接口,業務元件做具體實作,在IProvider中定義接口實作路徑,所有Activity和Fragment路徑以及給其他業務元件調用的方法。common_component中定義 ARouterManager 傳回各個業務元件IProvider。

common_component/ARouterManager

Android篇丨震坤行移動端元件化演進之路

common_component/ISkuProvider

Android篇丨震坤行移動端元件化演進之路

sku_component/SkuProvider

Android篇丨震坤行移動端元件化演進之路
Android篇丨震坤行移動端元件化演進之路

sku_component/CategoryFragment

Android篇丨震坤行移動端元件化演進之路

4. ARouter漸進式改造

1. 如上所示,定義全局IAppProvider接口并将AppProvider實作放在App Module中,在元件化完成後該類會被删除;

Android篇丨震坤行移動端元件化演進之路

2. 在IAppProvider中定義所有的路由路徑,并添加到App Module裡的Activity和Fragment中,這樣項目就通過ARouter成功運作起來了;

3. 抽離業務元件并定義對應的IBussinessProvider(如ISkuProvider)并定義我們需要的元件通信方法,将抽離到的業務元件對應的路由路徑從IAppProvider抽離到IBussinessProvider中;

4. 需要注意ARouter允許一個Module中存在多個分組,但是不允許多個Module中存在相同的分組, 在業務元件抽離的過程中要注意不要使用相同的分組。

5.消息總線

有了路由架構來使得元件之間通信,為什麼還需要消息總線呢?

上面提到ARouter是通過接口下沉來實作通信,接口調用方需要依賴這個接口并且知道哪個元件實作了這個接口,而消息總線發送方隻需要發送消息,根本不用關心是否有人訂閱了這個消息,即不需要了解其他元件的情況,使得元件之間實作更徹底的解耦。基于接口的方式隻能進行一對一的調用,但是基于 消息總線的方式能夠提供多對多的通信。

但是消息總線的這些優點也帶了對應的缺點。由于發送者和接收者之間互相不知道,導緻邏輯分散,出現問題後很難定位;消息總線能很友善的實作App中跨⻚面的互動,隻需要通過發送消息即可,不用理 會麻煩的 startActivityForResult 和 onActivityResult ,這就會導緻消息随意發送,大量濫用。使得後續代碼維護很麻煩,特别是那種跨元件的消息。這裡就需要在項目組中制定相關規範,控制好消息的發送。

6.事件總線EventBus

EventBus 是一個 Android 事件釋出/訂閱架構,通過解耦釋出者和訂閱者簡化事件傳遞。既可以用于Android四大元件間的通訊,也可以用于異步線程和主線程間的通訊,支援指定事件處理的線程和優先級,可以發送與粘性廣播類似的粘性事件。簡化代碼,消除依賴關系,加速應用程式開發。

Android篇丨震坤行移動端元件化演進之路

1. 基類中進行綁定

在BaseActivity和BaseFragment中添加EventBus的注冊和反注冊,為了友善我們使用注解來定義子類是否需要與EventBus綁定。

Android篇丨震坤行移動端元件化演進之路
Android篇丨震坤行移動端元件化演進之路
Android篇丨震坤行移動端元件化演進之路

2. 對Event封裝

Android篇丨震坤行移動端元件化演進之路

Event傳入泛型指具體的事件類,code用來業務區分

Android篇丨震坤行移動端元件化演進之路

3. 使用

發送消息:

Android篇丨震坤行移動端元件化演進之路

接收消息:

Android篇丨震坤行移動端元件化演進之路

注意事項:

◆不要将所有事件都通過EventBus進行發送;

◆在Activity和Fragment中使用,需要反注冊,以免發生記憶體洩露。

2.5 元件如何獨立運作

1. 元件運作模式切換

Android Studio中的Module主要有兩種屬性,分别為:

1. application屬性,可以獨立運作的Android程式,也就是我們的APP

Android篇丨震坤行移動端元件化演進之路

2. library屬性,不可以獨立運作,一般是Android程式依賴的庫檔案;

Android篇丨震坤行移動端元件化演進之路

Module的屬性是在每個元件的檔案中配置的,當我們在元件模式開發時,業務元件應處于application屬性,這時的業務元件就是一個Android App,可以獨立開發和調試;而當我們轉換到內建模式開發時,業務元件應該處于library 屬性,這樣才能被我們的app殼工程所依賴,組成一個具有完整功能的APP。

當我們用AndroidStudio建立一個Android項目後,Gradle會在項目的根目錄中生成一個檔案gradle.properties:在Android項目中的任何一個build.gradle檔案中都可以把gradle.properties中的常量讀取出來;那麼我們可以通過在gradle.properties中定義一個常量值 isRunAlone 來控制元件是否單獨運作(true為是,false為否):

Android篇丨震坤行移動端元件化演進之路

然後我們在業務元件的build.gradle中讀取 isRunAlone ,但是 gradle.properties 還有一個重要屬性:gradle.properties 中的資料類型都是String類型,使用其他資料類型需要自行轉換,也就是說我們讀到是個String類型的值,而我們需要的是Boolean值,代碼如下:

Android篇丨震坤行移動端元件化演進之路

1. 元件單獨運作配置

元件單獨運作是需要配置一些相關資訊的,如Application、啟動⻚面等。

1. 通過修改SourceSets中的屬性來指定AndroidManifest檔案:

SourceSets屬性可以指定哪些源檔案或者檔案夾下的源檔案要被編譯,哪些源檔案要被排除。是以我們也可以利用上面 isRunAlone 參數來配置不同環境所需要的資源和依賴檔案。

Android篇丨震坤行移動端元件化演進之路

2. 通過使用debug目錄來指定代碼和資源檔案:

由于元件單獨運作一般隻用于開發階段,debug目錄隻有在元件單獨運作才生效,作為library打包到主app或者單獨打aar包時會自動排除debug目錄下的代碼和資源,對正式代碼無污染

◆在component/src下建立debug檔案夾;

◆在debug檔案夾下建立2個檔案夾:java和res;

◆在java檔案夾下建立所需要的Application、launchActivity等;

◆在res檔案夾下;

◆将Application、launchActivity等注冊到AndroidManifest.xml中。

在整個App運作過程中,各個業務元件之間是不互相依賴的、完全隔離的,他們之間由于打包到一起是可以通過路由架構互動的。但是我們在獨立運作過程中是沒有其他業務元件的,如果有需要其他業務元件則需要添加到依賴配置裡面,如項目中使用者登入涉及到很多東⻄,在業務元件單獨運作中沒法簡單實作,這也是一般會将注冊登入單獨作為一個元件抽離出來,友善其他業務元件獨立運作所依賴。這裡也是需要isRunAlone參數來差別。

Android篇丨震坤行移動端元件化演進之路

3

演進之路總結

震坤行Android項目中有Lombok、ButterKnife,而且也正在往Kotlin遷移中,是以中間還是踩了一些坑的。在元件化完成後,項目組也會慢慢将各個元件完全Kotlin并移除掉Lombok和Butterknife。

◆Lombok和Kotlin

由于編譯順序,在編譯Kotlin的時候Java代碼還沒有編譯,是以Lombok沒有自動生成代碼導緻失敗。目前項目中的做法是将Lombok放到Library中(主要是實體類)。由于使用了Kotlin不再需要Lombok的能力,後續項目中會逐漸移除Lombok。

◆ButterKnife

具體可以參考ButterKnife in Library projects,如果項目中使用了apply plugin: 'kotlin- kapt則需要将annotationProcessor修改為kapt butterknife_compiler。由于使用了Kotlin不再需要ButterKnife的能力,後續項目中會逐漸移除ButterKnife。

◆統一配置檔案一定要提前準備好,不然後續各種麻煩,詳細參⻅統一配置檔案

◆全局變量設定

Android項目中用到Application的地方很多,對于業務元件的話可以通過 ParentApplication來獲得,而在基礎Lib中可以定義一個工具類在程式啟動時将Application傳遞給它,供基礎Lib中使用。

◆ARouter的使用

先将項目全部轉換成Router跳轉,然後進行業務抽離,這樣就相當于進行了一次解耦,業務抽離過程會快速很多。

◆元件獨立運作可以先不實施,在需要的時候各業務線實作即可

最終項目結構圖

Android篇丨震坤行移動端元件化演進之路

component_sku結構圖

Android篇丨震坤行移動端元件化演進之路

項目元件化的過程也是對項目業務進行梳理過程,業務劃分更加清晰,新人接手項目更加容易,按照元件配置設定開發任更加友善;項目的可維護性更強,提高了開發效率;出現問題了也更好排查,某個元件出現問題直接對該元件進行處理即可;各元件進行需求變更或者代碼重構等都不會在畏首畏尾,擔心會影響到其他子產品,将影響控制在元件内。

4

本篇文章參考

1. Android元件化方案及元件消息總線modular-event實戰

2. 有贊微商城 Android 元件化方案

3. 知乎 Android 用戶端元件化實踐

4. 關于Android業務元件化的一些思考

5. Kotlin+buildSrc for Better Gradle Dependency Management

6. WMRouter:美團外賣Android開源路由架構

7. ARouter 阿裡路由架構

8. CC(業界首個支援漸進式元件化改造的Android元件化架構)

9. 安居客 Android 項目架構演進

10. 美團外賣Android平台化架構演進實踐

11. 從智行 Android 項目看元件化架構實踐

來源-微信公衆号:産品技術團隊

出處:https://mp.weixin.qq.com/s/FeH2Lor620U5VZe8I84s6w

繼續閱讀