背景
因爲58APP是一個比較龐大的工程,涉及業務線較多,除非特殊狀況58APP每一年只會有一次統一升級各SDK的計劃,包括第三方SDK,構建環境等。本次就介紹一下升級Android構建工具的歷程。html
Android APP的構建通常涉及了三個工具,Android plugin、Gradle、Build Tools。58APP也不例外,在依賴於這三個工具同時咱們也有自定義的打包插件,自定義插件用於在構建時對構建過程進行自定義,構建過程當中運行一段咱們的代碼,從而達到咱們的目的。8App中這三個工具的版本分別以下:java
![94DF1576-9793-4A3F-9193-7C8626627FF4.png 94DF1576-9793-4A3F-9193-7C8626627FF4.png](http://static.javashuo.com/static/loading.gif)
其中最主要的版本號爲Android plugin,其餘兩個工具的版本號根據Android plugin的版本號的依賴而決定。Android plugin只規定了Gradle和Build Tools的最低版本,所以理論上咱們能夠直接升級Gradle和Build Tools的版本號。而對打包效率功能影響最大的是Android plugin,所以不能隨意升級,必須通過詳細調研。android
目前Android plugin版本爲3.0,該版本包含了各類解決大型項目性能問題的更改,具體變動此處再也不贅述。git
Android plugin版本變化梳理
最新的Android plugin版本爲3.3.1(筆者升級時爲最新版本,目前已更新至3.4.x),Android plugin版本號命名的規則大體以下:github
- 版本號一共爲3位
- 當發生重大升級時更新第一位版本號
- 當發生較大升級時更新第二位版本號
- 當修復bug或者其餘的一些補丁時更新第三位版本號
所以3.3.1爲3.3.0版本的小版本升級,基本與3.3.0一致。因爲3.0到3.3.0只發生了3個較大版本升級,所以咱們來分別分析一些這三個較大版本升級的變化。apache
3.1.0 (2018.3)
依賴狀況:canvas
![CCCF276C-CE71-4AAC-A7EA-4C1A7FD123AB.png CCCF276C-CE71-4AAC-A7EA-4C1A7FD123AB.png](http://static.javashuo.com/static/loading.gif)
新的Dex編譯器D8
默認狀況下,Android Studio如今使用名爲D8的新DEX編譯器。 DEX編譯是將.class字節碼轉換爲Android Runtime(或Dalvik)的.dex字節碼的過程。 與以前的編譯器(稱爲DX)相比,D8編譯速度更快,輸出更小的DEX文件,同時具備相同或更好的應用運行時性能。api
D8不會影響正常的開發流程,若是不須要能夠配置如下代碼關閉D8編譯器:緩存
android.enableD8=false
其餘行爲變化
- 在構建多個平臺不一樣ABI的APK時,默認狀況下,插件再也不爲如下ABI生成APK:mips,mips64和armeabi。若是要構建以這些ABI爲目標的APK,則必須使用NDK r16b或更低版本並在build.gradle文件中指定ABI,以下所示:
![5861A858-27FF-4FC0-A8BA-F60F6E0FD4D8.png 5861A858-27FF-4FC0-A8BA-F60F6E0FD4D8.png](http://static.javashuo.com/static/loading.gif)
- 在爲Android Instant App構建配置APK時,語言配置拆分如今默認按根語言分組。 例如,若是您的應用包含zh-TW或zh-CN語言環境的資源,Gradle將以zh語言配置拆分打包這些資源。 您可使用include屬性定義本身的組來覆蓋此行爲,以下所示:
![03258DBA-AE62-404A-8DC0-D49B8CB34596.png 03258DBA-AE62-404A-8DC0-D49B8CB34596.png](http://static.javashuo.com/static/loading.gif)
- Android插件的構建緩存如今去除超過30天的緩存條目。
- 將「auto」傳遞給resConfig再也不自動選擇要打包到APK中的字符串資源。 若是繼續使用「auto」,插件將打包您的應用及其依賴項提供的全部字符串資源。 所以,您應該指定但願插件打包到APK中的每一個區域設置。
- 因爲本地模塊不能依賴於應用程序的測試APK,所以使用androidTestApi配置而不是androidTestImplementation將依賴項添加到已檢測的測試中會致使Gradle發出如下警告:
![BB8831A3-C715-4747-B928-C264E423099B.png BB8831A3-C715-4747-B928-C264E423099B.png](http://static.javashuo.com/static/loading.gif)
Bug修復
- 修復了Android Studio沒法正確識別複合構建中的依賴項的問題。
- 修復了在單個構建中屢次加載Android插件時出現項目同步錯誤的問題 - 例如,當多個子項目在其buildscript類路徑中包含Android插件時。
3.2.0 (2018.9)
依賴狀況:
![A64F9B24-CB82-41F4-9FB8-3CC84E74A917.png A64F9B24-CB82-41F4-9FB8-3CC84E74A917.png](http://static.javashuo.com/static/loading.gif)
新功能
- 支持構建Android應用程序包:應用程序包是一種新的上傳格式,包括全部應用程序的已編譯代碼和資源,同時推遲APK生成並簽名到Google Play商店。 您再也不須要構建,簽名和管理多個APK,而且用戶能夠得到針對其設備進行優化的較小下載。 要了解更多信息,請閱讀關於Android App Bundles。
- 使用註解處理器時支持提升增量構建速度:AnnotationProcessorOptions DSL如今擴展了CommandLineArgumentProvider,它使您或註釋處理器做者可以使用增量構建屬性類型註釋來註釋處理器的參數。 使用這些註釋能夠提升增量和緩存清理構建的正確性和性能。 要了解更多信息,請閱讀註釋處理器的Pass參數。
-
AndroidX的遷移工具:當使用Android Gradle插件3.2.0和Android 3.2及更高版本時,您能夠經過從菜單欄中選擇Refactor> Migrate to AndroidX來遷移項目的本地和Maven依賴項以使用新的AndroidX庫。 使用此遷移工具還會在gradle.properties文件中將如下標誌設置爲true:性能優化
- android.useAndroidX:設置爲true時,Android插件使用相應的AndroidX庫而不是支持庫。 若是未指定此標誌,則默認狀況下插件會將其設置爲false。
- android.enableJetifier:當設置爲true時,Android插件會自動遷移現有的第三方庫,經過重寫其二進制文件來使用AndroidX。 若是未指定此標誌,則默認狀況下插件會將其設置爲false。 只有當android.useAndroidX也設置爲true時,才能將此標誌設置爲true,不然會出現構建錯誤。
-
新的代碼縮減器,R8:R8是一種用於代碼縮減和混淆的新工具,它取代了ProGuard。 您能夠經過在項目的gradle.properties文件中包含如下內容來開始使用R8的預覽版本:
android.enableR8 = true
其餘行爲變化
- 如今默認狀況下啓用D8 Desugaring。
- AAPT2如今在Google的Maven倉庫中。 要使用AAPT2,請確保build.gradle文件中包含google()依賴項,以下所示:
![E193CC63-7F68-46E3-86EE-95498CCB9A49.png E193CC63-7F68-46E3-86EE-95498CCB9A49.png](http://static.javashuo.com/static/loading.gif)
- 如今默認啓用本機multidex。 在將應用程序的調試版本部署到運行Android API級別21或更高級別的設備時,之前版本的Android Studio啓用了本機multidex。 如今,不管您是部署到設備仍是構建APK以進行發佈,Android Gradle插件均可覺得設置minSdkVersion = 21或更高版本的全部模塊啓用原生multidex。
- 該插件如今強制執行依賴protobuf插件(0.8.6),Kotlin插件(1.2.50)和Crashlytics插件(1.25.4)的最低版本。
- 功能模塊插件com.android.feature如今在指定模塊名稱時僅強制使用字母,數字和下劃線。 例如,若是您的功能模塊名稱包含破折號,則會出現構建錯誤。 此行爲與動態要素模塊插件的行爲相匹配。
Bug 修復
- JavaCompile如今能夠在具備數據綁定的項目中緩存。
- 更好地編譯避免庫模塊使用數據綁定。
- 若是因爲某些不可預測的構建錯誤而在早期版本中禁用了按需配置,則如今能夠從新啓用按需配置。(3.0.x or 3.1.x在Gradle 4.6及以上時按需配置不能使用)
3.3.0 (2019.2)
依賴狀況:
![07C8228E-808A-4F48-B229-01B4C60DF8F5.png 07C8228E-808A-4F48-B229-01B4C60DF8F5.png](http://static.javashuo.com/static/loading.gif)
新功能
- 改進的類路徑同步:在解析運行時和編譯時類路徑的依賴關係時,Android Gradle插件會嘗試修復出如今多個類路徑中的依賴關係的某些下游版本衝突。
例如,若是運行時類路徑包含庫A版本2.0而且編譯類路徑包含庫A版本1.0,則插件會自動將編譯類路徑的依賴性更新爲庫A版本2.0以免錯誤。
可是,若是運行時類路徑包含庫A版本1.0而且編譯包含庫A版本2.0,則插件不會將編譯類路徑上的依賴項降級爲庫A版本1.0,而且您將收到錯誤。 要了解更多信息,請參閱修復類路徑之間的衝突。
-
使用註解處理器時改進了增量Java編譯:此更新經過在使用註釋處理器時改進對增量Java編譯的支持來減小構建時間。
- 對於使用Kapt(大多數僅限Kotlin項目和Kotlin-Java混合項目)的項目:即便使用數據綁定或retro-lambda插件,也會啓用增量Java編譯。 Kapt任務的註釋處理還沒有增量。
-
對於不使用Kapt的項目(僅Java項目):若是您使用的註釋處理器都支持增量註釋處理,則默認狀況下會啓用增量Java編譯。 要監控增量註釋處理器的採用狀況,請觀看Gradle issue 5277。
可是,若是一個或多個註釋處理器不支持增量構建,則不會啓用增量Java編譯。 相反,您能夠在gradle.properties文件中包含如下標誌:
android.enableSeparateAnnotationProcessing=true
當您包含此標誌時,Android Gradle插件會在單獨的任務中執行註釋處理器,並容許Java編譯任務以遞增方式運行。
-
使用過期的API時更好的調試信息:當插件檢測到您使用的API再也不受支持時,它如今能夠提供更詳細的信息,以幫助您肯定API的使用位置。 要查看其餘信息,您須要在項目的gradle.properties文件中包含如下內容:
android.debug.obsoleteApi=true
您還能夠經過從命令行傳遞-Pandroid.debug.obsoleteApi = true來啓用該標誌。
- 您能夠從命令行對動態要素模塊運行檢測測試。
其餘行爲變化
- 延遲任務配置:該插件如今使用Gradle的新任務建立API來避免初始化和配置完成當前構建不須要的任務(或不在執行任務圖上的任務)。 例如,若是您有多個構建變體,例如「發佈」和「調試」構建變體,而且您正在構建應用程序的「調試」版本,則該插件可避免初始化和配置「發佈」版本的任務。
在Variants API中調用某些舊方法(例如variant.getJavaCompile())可能仍會強制執行任務配置。 要確保您的構建針對延遲任務配置進行了優化,請調用新方法,而不是返回TaskProvider對象,例如variant.getJavaCompileProvider()。
若是您執行自定義構建任務,請了解如何適應Gradle的新任務建立API。
- 對於給定的構建類型,當設置useProguard爲false時,插件如今使用R8而不是ProGuard來縮小和混淆應用程序的代碼和資源。 要了解有關R8的更多信息,請閱讀Android Developer's Blog中的此博客文章。
- 庫項目的R類生成更快:之前,Android Gradle插件會爲每一個項目的依賴項生成一個R.java文件,而後與應用程序的其餘類並行編譯這些R類。 該插件如今直接生成包含應用程序編譯的R類的JAR,而無需先構建中間的R.java類。 此優化能夠顯着提升包含許多庫子項目和依賴項的項目的構建性能,並提升Android Studio中的索引速度。
- 該插件強制指定某些第三方插件的最低版本。
- 單變量項目同步:將項目與構建配置同步是讓Android Studio瞭解項目結構的重要一步。 可是,對於大型項目而言,此過程可能很是耗時。 若是您的項目使用多個構建變體,您如今能夠經過將項目同步限制爲僅當前選擇的變體來優化項目同步。
您須要在Android Gradle Plugin 3.3.0或更高版本中使用Android Studio 3.3或更高版原本啓用此優化。 知足這些要求後,IDE會在您同步項目時提示您啓用此優化。 默認狀況下,新項目也會啓用優化。
要手動啓用此優化,請單擊文件>設置>實驗> Gradle(Android Studio>首選項>實驗> Mac上的Gradle),而後選中Only sync the active variant複選框。
- 自動下載缺乏的SDK包:此功能已擴展爲支持NDK。 要了解更多信息,請閱讀使用Gradle自動下載缺乏的軟件包。
Bug修復
- 儘管Jetifier已啓用,但構建過程調用android.support.v8.renderscript.RenderScript而不是AndroidX版本
- 由androidx-rs.jar引發的衝突包括靜態捆綁的annotation.AnyRes
- 使用RenderScript時,您再也不須要在build.gradle文件中手動設置Build Tools版本
升級動力
Dex編譯器D8
3.1.0版本新增了名爲D8的Dex編譯器,聽說D8編譯速度更快,輸出更小的DEX文件,同時具備相同或更好的應用運行時性能。
代碼縮減和混淆的新工具R8
R8是一種用於代碼縮減和混淆的新工具,它取代了ProGuard。目前處於預覽版,默認不啓用。
根據官方測試數據,啓用R8與D8後,編譯速度有所提高,可是dex文件減少並不明顯,詳細見下圖所示:
![3863B659-F545-45F0-B3E7-9FD20E295FCA.png 3863B659-F545-45F0-B3E7-9FD20E295FCA.png](http://static.javashuo.com/static/loading.gif)
![010092E5-13B4-423B-A5D1-0CFA65A2B9A5.png 010092E5-13B4-423B-A5D1-0CFA65A2B9A5.png](http://static.javashuo.com/static/loading.gif)
![000CF6AC-3ACA-4E3A-ABB1-071FB1B99CC9.png 000CF6AC-3ACA-4E3A-ABB1-071FB1B99CC9.png](http://static.javashuo.com/static/loading.gif)
因爲R8處於預覽版,並且對減小包大小的效果並不明顯所以本次升級並不啓用R8。
其餘性能優化
- 使用註解處理器時支持提升增量構建速度。
- 延遲任務配置,避免初始化和配置完成當前構建不須要的任務。
- 庫項目的R類生成更快,3.3版本插件直接生成包含應用程序編譯的R類的JAR,而無需先構建中間的R.java類。
- 單變量項目同步:Android Studio 3.3與3.3版本插件配合時優化了項目同步功能,項目使用多個構建變體時,能夠經過將項目同步限制爲僅當前選擇的變體來優化項目同步速度。
開始升級
gradle語法變動
- 錯誤:插件3.1版本開始instrumentTest修改成androidTest,影響全部包含instrumentTest語法的庫
- 警告:android.enableAapt2=true屬性已失效,默認使用aapt2
- 警告:android.enableDesugar=false屬性已失效,默認true
- 依賴語法變動:
![2B03C99A-29EF-49C8-A625-9D748800C6B7.png 2B03C99A-29EF-49C8-A625-9D748800C6B7.png](http://static.javashuo.com/static/loading.gif)
multidex問題
- 生成的maindexlist文件目錄以及名稱修改,致使自定義multidex.gradle中的hook失效,提示 multiDexBuildMainlistTask can not find the maindexlist.txt file。因爲代碼中經過名稱獲取目錄,所以將判斷的名稱由maindexlist.txt改成mainDexList.txt解決此問題,代碼以下:
![B2A62CA8-D92B-4DAC-928E-3C54ADCBFF56.png B2A62CA8-D92B-4DAC-928E-3C54ADCBFF56.png](http://static.javashuo.com/static/loading.gif)
- 打包時提示maindex field超出,使用dex-member-list工具分析,發現結果以下:
![BEB59020-AD65-41FB-A5EF-4CC2DEEDEF79.png BEB59020-AD65-41FB-A5EF-4CC2DEEDEF79.png](http://static.javashuo.com/static/loading.gif)
再經過android-classyshark分析,所有是各個包下的R文件超出,所以添加如下一些不須要的R文件至main-exclude-libs-list.txt
![A1487170-8A94-4DD0-A5A4-DD03CCFFE8D8.png A1487170-8A94-4DD0-A5A4-DD03CCFFE8D8.png](http://static.javashuo.com/static/loading.gif)
API問題:
- junit.framework.Assert類不存在,因爲使用的地方都是斷言,所以進行相似如下修改:
![E221056D-AA88-43DE-B734-774A0B754EAB.png E221056D-AA88-43DE-B734-774A0B754EAB.png](http://static.javashuo.com/static/loading.gif)
- canvas.save(Canvas.ALL_SAVE_FLAG);方法不存在,僅存在無參方法canvas.save(),所以修改成canvas.save()
- canvas類中除ALL_SAVE_FLAG之外的其餘FLAG都不可用了,所以涉及到canvas.saveLayer方法中的FLAG參數大部分是錯誤的,修改成ALL_SAVE_FLAG,源碼的實現中此方法的saveFlags也會被替換爲ALL_SAVE_FLAG
![2EAF20BA-AE5F-4D0F-A8A3-A828B39B26AA.png 2EAF20BA-AE5F-4D0F-A8A3-A828B39B26AA.png](http://static.javashuo.com/static/loading.gif)
- java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/message/BasicNameValuePair httpclient相關類找不到,google發現新增一個配置在manifest文件
- Permission Denial: startForeground from pid=2381, uid=10351 requires android.permission.FOREGROUND_SERVICE 新增權限android.permission.FOREGROUND_SERVICE。因爲項目中只有一處com.wuba.debug.floatball.FloatBallService中使用startForeground,並且此類已再也不使用,所以刪除此類。
jar包資源問題
一些不規範的jar包中存在res資源目錄(如百度地圖sdk),在Android plugin 3.0之前是能夠將jar包中的res資源打包到apk中,從Android plugin 3.0之後就沒法打包進apk。因爲58APP中包含res的jar包還有很多,對於這種狀況咱們的解決辦法是自定義構建插件在構建過程當中將jar包中的res複製到生成目錄。這次升級至3.3.1後自定義的plugin因爲版本升級後一些task變化目前已失效,所以從新編寫插件代碼來實現此功能,主要代碼以下:
![C7689E79-DE69-49E2-B269-075C8FFC27E1.png C7689E79-DE69-49E2-B269-075C8FFC27E1.png](http://static.javashuo.com/static/loading.gif)
遺留問題
目前項目同步時會出現如下警告(雖然不影響編譯,可是看起來仍是不舒服):
![A740D31B-0831-421E-9F87-65788348F1BC.png A740D31B-0831-421E-9F87-65788348F1BC.png](http://static.javashuo.com/static/loading.gif)
前兩個警告,提示咱們getJavaCompiler會在2019年末刪除,可是getJavaCompileProvider的返回值與getJavaCompiler不一致,目前暫沒有特別好的辦法,後續有好的辦法再解決。
最後一個警告是提示咱們將compile替換爲implementation或者api,可是咱們項目中都已替換,警告排查是咱們android-aspectjx插件語法致使,後續android-aspectjx升級後便可解決。
升級先後速度對比
通過測試升級後編譯速度提高了大約33%,測試方式爲clean後的全源碼編譯,下面是編譯數據:
![ADE37DFA-60CD-4386-9186-5AF27859650E.png ADE37DFA-60CD-4386-9186-5AF27859650E.png](http://static.javashuo.com/static/loading.gif)