背景
隨著app的持續迭代更新,新功能的持續整合等原因。專案中的library project越來越多。
特別是最近一兩年來。流行起來了元件化,這更是直接增加了很多的本地library project的數量。導致每次需要打包apk執行時,都會需要對所有module都進行再次編譯。嚴重影響打包速度。
加速外掛簡介
Speedup是一款專用於對多module環境下進行打包加速的gradle外掛。主要依賴的基本原理有兩個:
- 動態依賴替換
- 打包任務特性
外掛地址
動態依賴替換
我們先來看看常規多module環境下的打包流程:
從上圖可以看到。每次打包的時候。都會經過以下幾個步驟:
- 對依賴的module進行進行打包編譯,生成各自library的aar依賴。
- 對aar依賴進行合併。包括classes.dex的合併、資源合併等。
- 將合併後的檔案、資源進行打包,生成apk。
而如果。我們使用maven依賴的方式進行依賴的話。編譯流程就會優化成下圖所示的方式:
可以看到。與上面直接使用module依賴的方式相比:使用Maven倉庫依賴的方式,由於maven倉庫中提供的直接就是已打包好的aar依賴,所以直接幹掉了module -> aar打包編譯的過程,起到了加速編譯的作用!
所以,現在我們就可以得出第一個結論:使用maven依賴比使用module依賴更快速!
所以這個時候。很多人的常規做法就是。想辦法將這些module依賴的library,編譯打包釋出到各自的遠端中央倉庫中去,這樣就能避免每次執行的時候都去進行額外編譯,從而節省時間了。
但是這種做法存在幾個問題:
- 絕大部分library並不穩定。需要經常修改使用,若放到遠端倉庫,修改起來很麻煩
- 遠端倉庫的version版本號唯一,每次進行修改後都會需要更新升級版本號再發布。多人協作時容易造成混亂且也不容易維護
所以將module打包釋出到遠端倉庫的做法,並不可取!
現在我們來考慮一下,如果將倉庫設定在本地會怎樣?也就是說使用本地maven倉庫進行依賴管理。
我們來列舉一下這裡使用本地依賴的優點:
-
本地倉庫中version不唯一:
即可以做到同版本覆蓋釋出,修改程式碼後直接重新發布即可,不需要提升version版本。這也正是遠端倉庫所做不到的。可避免頻繁升級改動。
-
本地倉庫不與網路打交道。
讀取依賴更快速。事實上,遠端倉庫中所依賴的庫,也是先存放到本地倉庫中去再進行讀取的。
可以看到,使用本地倉庫,可以完美的解決頻繁發包的問題!因為如果你的library不需要頻繁修改的話,你肯定也早就已經把它單獨拆出去放遠端倉庫中去了~
所以我們只要解決依賴不統一的問題。那麼這個加速方案就算是徹底打通了!
打包任務特性
打包任務這裡特指的assembleXXX任務。這個任務相信大家平時也見過無數次了。
此任務是由android外掛所提供的,編譯、合併、打包一條龍的匯流排任務。平時我們點選執行按鈕、打包apk時。也是通過的呼叫此任務進行打包。打包後通過內建的adb命令將apk傳送到手機並執行安裝。
這個任務有個很重要的特性。這裡我稱之為按需載入特性
打個比方:假如我們使用的元件化開發模式,專案依賴鏈如下圖所示:
上圖所示的依賴鏈,對元件化不熟的可能會比較懵逼。對元件化有興趣的可以去這裡瞭解一下元件化的專案結構:聚美元件化實踐之路
這裡有三個可執行的殼工程。其各自殼工程的依賴鏈如下:
- [app shell 1] -> [component 1] -> [base library]
- [app shell 2] -> [component 2] -> [base library]
- [Main app] -> [component 1] & [component 2] -> [base library]
而按需載入特性是指:在進行打包任務過程中,打包任務只會觸發當前自身依賴鏈中的module進行編譯。而不會對別的非自身依賴鏈中的module進行編譯。
所以,當你執行app shell1的時候。這個時候會參與打包編譯的就是[app shell 1]、[component 1]、[base library]這三個module. 這就是按需載入特性
原理
基於以上分析,我建立了此Speedup外掛。利用gradle指令碼,方便的進行本地aar打包釋出,並通過對打包任務流程進行監聽,動態的將module依賴替換為本地倉庫中的maven依賴。
流程圖
這裡我畫了個簡單的流程圖。便於直觀的理解此gradle外掛的執行機制。
特性
- 原理簡單:整個外掛由三個檔案組成,並有詳細註釋,很容易理解
- 配置簡單:簡單得出乎你意料!
- 不同機器配置獨享,不衝突:
- 相容性好:實現邏輯依賴最簡單的api。最大程度降低gradle版本升級時sdk改動造成的影響。
使用方式
依賴配置
在專案根目錄下新增依賴並新增應用外掛
buildscript {
repositories {
// 新增jitpack倉庫地址
maven { url "https://jitpack.io" }
}
dependencies {
// 新增外掛依賴
classpath "com.github.yjfnypeu:Speedup:0.9"
}
}
// 應用外掛
apply plugin: 'speedup'
複製程式碼
屬性配置
Speedup支援配置三種屬性,均配置於專案根目錄下的local.properties檔案中。
-
speedup.enable:
是否啟用speedup外掛。預設為false,即不啟用。這樣做的目的是為了使得在發版機器上進行打包apk時。自動忽略speedup外掛。進行全量編譯
-
localRepo:
本地maven倉庫的地址。預設為專案根目錄下的_repo資料夾。若你需要使用別的地址。在此配置個本地地址即可
-
excludeModules:
由於我們開發的業務線的不同。很多時候我們是需要對部分的module每次都進行即時編譯的:比如說現在正在做的功能。是需要對某個lib進行頻繁修改的。所以這個時候。就需要使用此屬性。指定此lib不需要進行加速。那麼每次執行時。都會對此lib進行編譯。
把所有屬性全放置於local.properties中,出於以下幾點考慮:
- 每個人所需要使用的配置方案不一樣,放於local檔案中,可以起到配置獨立的作用。
- 發版apk不使用加速方案。一般打包發版apk的均執行在獨立的機器上.
配置示例
github上我提供了一個元件化demo地址, 此demo中有新增此外掛配置使用。以下內容建議結合demo進行檢視。
由於本地配置是放置於local.properties檔案當中。所以預設依賴下來是沒有開啟啟用此外掛的。可以參考下方的配置:放入自身的local.properties中去。
speedup.enable=true
# 當需要排除多個module時,中間用英文的逗號隔開
excludeModules=:usercenter:componentusercenter
複製程式碼
當配置speedup.enable為true,並且同步完成之後。你就可以在gradle任務列表中發現一組speedup任務:
可以看到。speedup任務組中。含有大量的uploadXXX任務提供使用。這些任務都是在同步時根據各個library module自動生成的上傳任務。主要目的是將本地的library module方便的進行打包、編譯。併發布到本地的倉庫中去。提供使用。
當你使用upload進行本地打包釋出後,你就可以在專案根目錄下的_repo資料夾下找到對應的打包出來的aar:
然後,你可以試試直接執行。並且觀察右下角的Gradle Console控制檯輸出日誌。注意看所有執行的任務列表。就會發現:執行的gradle任務明顯減少了,並且被打包到本地中的module。並沒有被再次進行編譯。
這是因為已經在執行時。動態的將對應的module替換為使用本地倉庫中的aar地址了。
以上即是此外掛的整個用法示例。可以總結為以下幾步:
- 新增依賴、配置
- 執行upload任務進行本地釋出
- 直接執行。
upload任務列表說明
Speedup外掛任務中,很重要的一點即是生成的upload任務列表了。此upload任務主要分為三類:
-
uploadAll
作用:用於對專案中所有的library(除了excludeModules所指定排除的module)進行打包釋出。此任務預設將會對專案下所有的module執行clean操作。所以可以保證打包出來的aar是不受資料汙染的
使用場景:在進行分支切換後、或者進行合併程式碼後。此時很多module的資料都有修改。可以使用此任務一次性全部重新打包上傳。
-
uploadForClean
作用:用於觸發專案下所有module的clean任務。將其build資料夾刪除。避免打包時出現資料汙染。
與AS自帶的clean任務不同。自帶的clean任務附帶了很多額外的任務,所以每次clean會很慢。而uploadForClean。只會觸發所有module自身的clean任務,對自身的build資料夾進行刪除,並無其他額外操作。這樣clean的速度會得到很大提升。
此任務一般來說外部不需要直接使用。主要是內部直接提供給uploadAll進行使用
-
uploadXXX
作用:用於對指定的module進行重新打包釋出。比如uploadbaselib。此任務代表對baselib重新進行打包釋出。