當前位置:
首頁 > 最新 > Android模塊化實踐

Android模塊化實踐

隨著APP的不斷迭代,業務越來越複雜,代碼量越來越多,單個APP的模式已開始影響開發效率,而且原來的單模塊很難進行業務遷移。所以決定採用模塊化/組件化的思想對APP進行重構。

組件化與模塊化

什麼是組件化/模塊化

組件化和模塊化是當前軟體開發中常用的與平台無關的的解耦手段,被廣泛應用在軟體的架構層面。這兩者通常是相輔相成,通過組合的方式來使用。它們只是架構方面的一種思想,在代碼的實現層面上沒有多大區別。個人覺得差別也就以下兩點:

1、復用性:組件更注重複用性,如開發中用到的網路組件,圖片組件等, 在每個項目中都可使用;

2、應用範圍:復用性決定了應用範圍,也就是說,組件通常指的是底層模塊,公共組件等。而模塊既可表示上層的業務,也可表示組件中的某個業務功能,如圖片組件中的緩存模塊,下載模塊等。所以模塊的應用範圍更廣。


在早期的Java開發中,提倡將整個項目結構按照程序的邏輯結構進行分層,比如表示數據的Dao層,表示控制器的Control層,表示View的View層,但是隨著業務的不斷迭代,發現這種分層方式有很大的弊端,代碼難以定位,且後期難以維護。隨後就出現了以業務結構劃分的模式,這種結構徹底解決了以上問題。所以當前的APP基本上使用的都是這種以業務劃分的模式,但隨著App的不斷迭代,業務變得越來越複雜,代碼量越來越多,維護也變得越來越困難。還有一個明顯的問題,Gradle在編譯的時候花費的時間越來越長,這大大降低了APP的開發效率。既然單個APP難以解決這個問題,那可以將項目進行拆分,每個人只需要負責開發、維護自己的模塊即可。如何拆分呢?使用模塊化技術按業務邏輯將APP進行劃分,使得這些被拆分出來的模塊可單獨運行,這樣就提高了編譯速度,降低了維護成本。

組件化/模塊化的優點:

1、解耦,重用;

2、降低維護成本,提高開發效率;


模塊化的項目結構

這次重構採用層次化的方式,模塊化的思想,對APP進行了徹底的重構,具體的項目結構如下圖所示:

從結構上來看,APP被劃分成5層,每層的功能具體如下:


這是一個空的項目,其中只包含了一個Application的子類和一個IntentService的子類,主要用來對APP中使用的各種組件進行初始化,IntentService的作用是為了提高APP冷啟動的速度,將各種組件的初始化放在後台線程非同步執行,這裡需要注意的是,對於在Applicaiton或SplashActivity中就會使用的組件,最好直接在Application中進行初始化,否則會拋出未初始化的異常。


這裡的業務層被劃分為Main模塊和其他模塊(至於劃分幾個模塊,根據自己APP的業務,選擇合適的粒度進行劃分即可)。這裡的Main模塊主要包括:新用戶引導頁,啟動頁,主頁。具體業務方面的頁面,都放在具體的業務模塊中。


公共組件層主要包括APP中使用的第三方組件,這些組件基本都是現在APP的通用功能,為上層的業務層提供支持。至於模塊劃分,雖然這些都是單獨的組件,但是每個做為一個Module未免有些繁瑣了,所以還是推薦放在一個Module中。在選擇第三方的庫時,需要做一定的調研,盡量選擇大公司,使用用戶多的SDK,同時在使用時最好封裝一下,這樣後面更換時也方便。


基礎業務層主要用來統一APP的代碼結構,UI風格等,主要包含以下三個方面:

Android組件的二次封裝

主要是對Activity/Fragment的封裝,提供了不需網路請求的BaseActivity/BaseFragment和需要網路請求的BaseProgressActivity/BaseProgressFragment, 為頁面的代碼提供統一的結構,頁面的樣式提供統一的風格。

業務通用UI

主要包含各種樣式的Dialog, 自定義View等,根據APP的設計風格提供統一的樣式;

圖片操作庫

圖片操作庫ImageSet是對圖片組件庫(包括Fresco, Glide, Universer ImageLoader)的封裝,同時提供了調用系統相機/相冊選擇&裁剪照片,類似微信選擇圖片的組件,圖片上傳,圖片壓縮等功能。這個小模塊其實也可放在Common組件層,只是覺得這裡面也有一些業務相關的功能,所以就放在了這一層。

當然,基礎業務層還還包括APP設計風格中需要用到的各種動畫,樣式,顏色值,尺寸值等資源。在進行業務開發時,統一使用這些資源,為後續修改整個APP風格提供可能。


這裡包含了一些通用的組件,包括各種常用的工具類,通用的UI庫,數據源的封裝(包括網路,文件,資料庫)。這是一個APP的基本架構,裡面包含的類基本不需要改動。所以在對工具類和通用UI進行定義時,需要考慮放置的位置是否準確。

以上是整個項目使用組件化/模塊化後基本結構的詳細介紹,但是在開發過程中還是遇到了很多問題。


遇到的問題

從Android工程的結構可以看出,app模塊和新建的其他Module的結構基本一致,最大的區別就是build.gradle的結構:

所以Module是否能夠運行,關鍵就在於plugin的類型。將新建Module的build.gradle中的"com.android.library" 改成 "com.android.application",同步之後選擇相應的模塊運行即可。

所以模式的切換隻需要根據條件進行判斷即可,我們可在gradle.properties中定義一個常量,控制Module的運行模式:

gradle.properties中定義IS_MODULE:

然後在Module的build.gradle中添加條件判斷:

這樣在進行模式切換時,只需要修改IS_MODULE的值即可。


APP在進行打包時,會將所有依賴的Module中的AndroidManifest文件進行合併,具體的合併規則參考合併多個清單文件。合併最基本的原則:只能有一個Application配置了name屬性,只能有一個Activity被配置成了主Activity。但是Module中如果不配置Applicaition中name屬性,就不能進行相應的初始化,如果不指定主Activity,APP也無法運行。這裡可使用兩種方案來解決:

1、Module依然當做library使用,Module中的AndroidManifest也不需要指定Application的name屬性和主Activity,直接載入Main模塊(SplashActivity作為主Activity),在SplashActivity中動態修改要載入的模塊。

2、在Module中其他路徑下新建一個AndroidManifest文件(其中為application標籤指定了name屬性,同時指定了主Activity),然後在build.gradle中根據IS_MODULE的值動態指定AndroidManifest的路徑,這樣Module在不同模式下使用不同的AndroidManifest文件就避免了合併出錯的問題。但這種方案每個Module需要提供單獨的Application類。

module/build.gradle


組件之間的通信可使用EventBus來實現, 可在每個模塊中新建一個Event類,將同模塊中通信需要的類都定義在這個Event類中。至於模塊間通信需要的類,可定義在公共組件層的Event類中(雖然不是很合理,但暫未想到更好的方案)。


在Module的開發中,我們可能需要引用ApplicationContext對象,但我們沒有Context對象,無法直接獲取到,此時可通過以下三種方式解決:

1、為需要ApplicationContext對象的類提供init靜態方法,在Application的onCreate中調用:

2、在Common組件層中提供一個繼承自Application的BaseApplication, 其中包含一個靜態的Context對象,在APP中重寫Application時繼承BaseApplication並對這個靜態的Context對象進行賦值;

3、在Common組件中提供一個ContentProvider組件,使用靜態的Context對象保存ApplicationContext對象(ContentProvider在系統創建Application對象後就會載入,具體細節查看APP的啟動過程的源碼);


模塊之間的依賴

除了Common組件層外,其他的層依賴原則——同層不依賴,下層不依賴上層

同層之間的依賴主要表現在業務層,這是不可避免的,但我們需要避免互相引用的問題,在業務層,我們可使用隱式跳轉的方式或使用阿里開源的路由框架ARouter實現。

模塊之外的依賴

Gradle3.4中提供了新的依賴配置的關鍵字:

implementation:依賴項在編譯時對所在模塊可用,在運行時對依賴該模塊的模塊可用;

api: 依賴項在編譯時對所在模塊可用,在編譯時和運行時對依賴該模塊的模塊可用;

Gradle3.4中提供的兩個關鍵字相當於對是之前的compile關鍵字進行細化。使用Gradle3.4之前的版本,引入依賴項時都使用compile關鍵字,compile關鍵字容易引起多次引入的問題。使用Gradle3.4之後的版本引入library時,對於外部不需要直接引用的library,最好使用implementation關鍵字,而對於外部需要引用的library, 可使用api(此時就相當於compile)。

資源的命名問題

為了避免資源衝突的問題,我們可在Module中的build.gradle配置資源名的前綴,一方面避免資源衝突,另一方面,也便於標識資源所在的模塊:


其他問題

Module中生成的資源Id不是final類型的,所以在onClick中不能使用switch語句塊,只能使用if...else if結構代替。


重構與版本迭代之間衝突是不可避免的問題。 通常重構的時候還需要版本迭代,此時可根據情況進行人員分配:

1、有足夠重構的時間:讓大多數人進行重構,只留一兩個進行版本迭代。重構完成後將新版本的內容進行合併;

2、沒有足夠重構時間:這種情況就不要做整體重構,而是根據模塊逐漸進行。

重構是一項體力活,也是一項出力不討好的活,畢竟重構之後不可避免地會出現很多Bug, 如果用戶量龐大,那後果可能很嚴重,所以在重構時最好閱讀一下原來的代碼,認真梳理一下業務邏輯再進行。


在重構的過程中對其中一個模塊嘗試使用了單Activity模式(頁面統一使用Fragment實現)。體驗感覺不錯,值得一試。

模塊化/組件化是一種與技術無關的架構思想,合理的應用可大大降低項目的耦合度。為了能夠快速開發一款新的應用,現已開源了一個通用的APP框架SimpleProject,目前只完成了Common組件層,隨後會逐漸進行完善。
喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 杭州田丁科技 的精彩文章:

TAG:杭州田丁科技 |