一、背景
Gralde版本與AGP(Android Gradle Plugin)版本具有一定的對應關系,原因在於AGP實質上作為Gradle的插件,依賴於Gradle作為宿主。因此,不同的AGP版本需要與相應的Gralde版本相匹配。他們之間版本的對應關系如下:
具體可以參照官方文檔:
developer.android.com/studio/rele…
當前項目中使用Gradle版本是4.4,AGP版本是3.1.0。為遵循漸進式策略,本次升級,目標是將Gralde版本升級到4.6,與之對應的,AGP升級到3.2.1。
主要考慮點:
1,漸進式升級,只從4.4升級到4.6,兼顧版本升級的同時,確保項目穩定性;
2,升級Gralde及AGP版本,有利於項目構建速度提升;
3,為不久后的升級到AndroidX做准備。
二、主要問題及解決
鑒於以往的感受:每次升級都不僅僅是單純的改一下版本號那么簡單,每次升級也都會遇到一些問題需要處理。
同樣的,此次升級,還是遇到了一些問題,主要記錄如下:
2.1 項目自定義插件中的方法簽名問題
1,問題:
修改gradle-wrapper.properties中Gradle版本,從4.4改成4.6
....
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
復制代碼
修改項目根目錄下的build.gradle文件,將AGP從3.1.升級到3.2.1。
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1' .... .... } 復制代碼
項目構建,發現出現如下錯誤:
App:cornProdReleaseMultidexKeep (Thread[Task worker for ':' Thread 4,5,main]) started. :App:cornProdReleaseMultidexKeep Caching disabled for task ':App:cornProdReleaseMultidexKeep': Caching has not been enabled for the task Task ':App:cornProdReleaseMultidexKeep' is not up-to-date because: Task has not declared any outputs. try to update manifest_keep.txt can't find getManifestKeepListFile method, exception:groovy.lang.MissingMethodException: No signature of method: com.android.build.gradle.internal.scope.VariantScopeImpl.getManifestKeepListFile() is applicable for argument types: () values: [] :App:cornProdReleaseMultidexKeep FAILED :App:cornProdReleaseMultidexKeep (Thread[Task worker for ':' Thread 4,5,main]) completed. Took 0.071 secs. 復制代碼
依據錯誤信息,是執行:App:cornProdReleaseMultidexKeeptask時,有調用到getManifestKeepListFile方法,因AGP版本升級,此處並沒有做好對應的向歷史兼容,直接報錯。
2,解決:
當前項目分包直接采用Google官方multidex方案。經查,此處是項目團隊自定義的插件,並提供了對外的配置項。目的是仿照tinker寫法,在分包時將對應的maindex配置項通過追加到manifest_keep.txt中,以保留到對應的maindexlist.txt,從而對應的類會被打包到maindex中,5.0以下機型不至於出現ClassNotFoundException相關的錯誤。插件中將對應任務hook到了multidex對應的task環節,相當於完成了額外的maindex配置。
主體實現邏輯如下:
OptMainDexTask.groovy
@TaskAction
def doAction() { def ext = project.extensions.findByName(OptMainDexExtension.NAME) as OptMainDexExtension if (!ext.enable) { project.logger.error("optMainDex enable set false!") return } project.logger.error("try to update manifest_keep.txt") StringBuffer lines = new StringBuffer() lines.append("#optMainDex.loader patterns here\n") Iterable<String> loader = ext.loader for (String pattern : loader) { if (pattern.endsWith("*")) { if (!pattern.endsWith("**")) { pattern += "*" } } lines.append("-keep class " + pattern + " {\n" + " <init>(...);\n" + "}\n") .append("\n") } File multiDexKeepProguard = null try { multiDexKeepProguard = applicationVariant.getVariantData().getScope().getManifestKeepListProguardFile() } catch (Throwable ignore) { try { multiDexKeepProguard = applicationVariant.getVariantData().getScope().getManifestKeepListFile() } catch (Throwable e) { project.logger.error("can't find getManifestKeepListFile method, exception:${e}") } } if (multiDexKeepProguard == null) { project.logger.error("auto add multidex keep pattern fail, you can only copy ${file} to your own multiDex keep proguard file yourself.") return } FileWriter manifestWriter = new FileWriter(multiDexKeepProguard, true) try { for (String line : lines) { manifestWriter.write(line) } } finally { manifestWriter.close() } } 復制代碼
cornPlugin.groovy
@Override
void apply(Project project) {
...
initDex(project)
...
}
def initDex(Project project) {
// 只支持Gradle 3.0以上
project.extensions.create(OptMainDexExtension.NAME, OptMainDexExtension)
def android = project.extensions.android
project.afterEvaluate {
OptMainDexExtension optMainDexExtension = project.extensions.getByName(OptMainDexExtension.NAME)
if (!optMainDexExtension.enable) { return } android.applicationVariants.all { variant -> def variantOutput = variant.outputs.first() def variantName = variant.name.capitalize() def variantData = variant.variantData boolean multiDexEnabled = variantData.variantConfiguration.isMultiDexEnabled() if (multiDexEnabled) { OptMainDexTask dexConfigTask = project.tasks.create("corn${variantName}MultidexKeep", OptMainDexTask) dexConfigTask.applicationVariant = variant // for java.io.FileNotFoundException: app/build/intermediates/multi-dex/release/manifest_keep.txt // for gradle 3.x gen manifest_keep move to processResources task dexConfigTask.mustRunAfter variantOutput.processResources def multidexTask = TaskUtil.getMultiDexTask(project, variantName) if (multidexTask != null) { multidexTask.dependsOn dexConfigTask } def collectMultiDexComponentsTask = TaskUtil.getCollectMultiDexComponentsTask(project, variantName) if (collectMultiDexComponentsTask != null) { dexConfigTask.mustRunAfter collectMultiDexComponentsTask } } } } } 復制代碼
OptMainDexTask中,是仿照之前tinker的寫法,現在tinker的新版本中,此處也已經經過了修改,因為tinker也將AGP版本升級到了3.2.1,並支持了更高版本的使用。
developer.android.com/studio/buil…
參照multidex官方文檔,實際上針對maindex的配置,是沒必要從插件中繞一道的,直接通過multiDexKeepFile或multiDexKeepProguard在主工程中配置即可。
對應的,刪除項目團隊自己的插件中的maindex此處邏輯,升級插件版本,主工程重新引入,去掉主工程對應的配置,並將需要保留在maindex的配置對應的配置好在multiDexKeepProguard文件中,並引入。
defaultConfig {
....
....
multiDexEnabled true multiDexKeepProguard file('multidex-config.pro') .... .... } 復制代碼
2.2 資源混淆工具AndResGuard異常
1,問題:
重新構建,出現如下錯誤信息:
parse to get the exist names in the resouces.arsc first com.tencent.mm.androlib.AndrolibException: Could not decode arsc file at com.tencent.mm.androlib.res.decoder.RawARSCDecoder.decode(RawARSCDecoder.java:74) at com.tencent.mm.androlib.ApkDecoder.decode(ApkDecoder.java:190) at com.tencent.mm.resourceproguard.Main.decodeResource(Main.java:96) .... 復制代碼
對應搜索,發現AndResGuard GitHub上也有不少人反饋,如對應issues: github.com/shwenzhang/…
2,解決:
原因在於升級后,系統默認升級了buildToolsVersion版本到28.0.3。從而導致對資源的解碼出現問題。項目當前buildToolsVersion版本為27.0.3,直接去除,采用AGP對應的默認buildToolsVersion版本配置。
同時,AndResGuard最新版本已經解決了此問題,直接升級版本到當前最新版本。
....
classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.17' .... 復制代碼
再次構建,構建成功。
核驗maindex分包相關的manifest_keep.txt和maindexlist.txt,與預期相符。在5.0以下手機上安裝測試,可以正常運行。
三、結語
Gradle升級到4.6,AGP相應升級到3.2.1,主要的更新點:
1,buildToolsVersion開始采取默認版本策略,AGP會按照其自身版本對應配置buildToolsVersion版本,即使因特別需要,人為也可以去指定buildToolsVersion版本,但版本范圍還是需要與AGP版本對應。
2,開始對遷移到AndroidX提供官方的直接支持。Google已經將android.*替換成androidx.*,並將不再更新維護原來的擴展庫,因此,盡早將項目的擴展庫遷移成AndroidX形式,也是必要的。
3,D8中的Desugaring(脫糖)默認開啟,為進一步使用Java8提供了支持。
4,新的代碼混淆工具,R8,以取代原有的ProGuard工具,當前默認未開啟,可以通過配置開啟。
android.enableR8 = true 復制代碼
Andorid項目開發中,涉及到各種版本的概念。項目不斷迭代的同時,官方開發工具、對應配套環境、工具庫、擴展庫等,也在不斷更新。為持續擁有更好的開發體驗、編譯速度和新功能支持,需要不斷的適時升級相應版本。
作者:HappyCorn
鏈接:https://juejin.im/post/5d6510796fb9a06b2e3d02c8
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
