Android Gradle的理解以及升級Gradle外掛3.0.1遇到的坑
Gradle簡介
開始填坑之前,先簡單聊聊gradle,Gradle其實就是一個構建專案的工具,也就是把那一個個檔案、資料夾按照一定的規則關聯起來,形成一個專案的工具,它其實不僅僅是用在AndroidStudio上。
我們在AS中用到的Gradle其實應該被叫做 Android Gradle Plugin,也就是安卓專案上的gradle外掛; Gradle外掛會有版本號,每個版本號又對應有一個或一些 Gradle發行版本(一般是限定一個最低版本),也就是我們常見的類似gradle-3.1-all.zip這種東西;
外掛版本 | Gradle版本 |
---|---|
1.0.0 - 1.1.3 | 2.2.1 - 2.3 |
1.2.0 - 1.3.1 | 2.2.1 - 2.9 |
1.5.0 | 2.2.1 - 2.13 |
2.0.0 - 2.1.2 | 2.10 - 2.13 |
2.1.3 - 2.2.3 | 2.14.1+ |
2.3.0+ | 3.3+ |
3.0.0+ | 4.1+ |
Android Studio 3.0 之後自動將外掛版本升級到3.0.0,所以我們也需要對應地把Gradle升級到4.1才行
另外, Android Gradle Plugin又會跟 Android SDK BuildTool有關聯,因為它還承接著AndroidStudio裡的編譯相關的功能,這也是我們要在專案的 local.properties 檔案裡寫明Android SDK路徑、在build.gradle 裡註明 buildToolsVersion 的原因。
所以 Android Gradle Plugin 本質上就是 一個AS的外掛,它一邊呼叫 Gradle本身的程式碼和批處理工具來構建專案,一邊呼叫Android SDK的編譯、打包功能,從而讓我們能夠順暢地在AS上進行開發。
升級Android Gradle Plugin到3.0.1的踩坑之旅
-
把工程目錄下的build.gradle中,將gradle外掛版本升級到3.0.1
dependencies { classpath 'com.android.tools.build:gradle:3.0.1' //這裡從2.2.2改到了3.0.1 }
-
點選同步gradle,報錯。 提示gradle-wrapper版本過低
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip //這裡從 3.1 改到了 4.1
然後同步gradle。 通常情況下你會發現速度特別慢,因為沒有翻牆,預設是從國外伺服器下載gradle; 這時候就可以強關AS,直接在網上找資源,把gradle-4.1-all.zip這個包,放在
C:\Users\你的使用者名稱.gradle\wrapper\dists\gradle-4.1-all\bzyivzo6n839fup2jbap0tjew
目錄中(最後那個亂碼資料夾名字每臺機器上不一樣),注意不需要手動解壓,然後重啟AS自動同步gradle即可;
-
重啟自動同步之後,又報錯。 提示有一些依賴庫無法正常引用,需要新增google maven 倉庫的依賴; 在工程目錄下的build.gradle 檔案加上maven依賴即可,
repositories { mavenCentral() jcenter() //加上下面這段 maven { url 'https://maven.google.com/' name 'Google' } }
-
再同步gradle,又報錯。 這次是提示沒有26.0.2版本的 SDK buildTool,然後直接在AS報錯彈框裡點選下載就可以了。現在SDK可以不用翻牆直接下載,速度還蠻快的。下載確認解壓之後再次同步gradle。
-
同步gradle,此時又會報錯:
Error:The specified Android SDK Build Tools version (25.0.0) is ignored, as it is below the minimum supported version (26.0.2) for Android Gradle Plugin 3.0.1. Android SDK Build Tools 26.0.2 will be used. To suppress this warning, remove "buildToolsVersion '25.0.0'" from your build.gradle file, as each version of the Android Gradle Plugin now has a default version of the build tools.
這個提示,說是需要去掉各個module的build.gradle中的 buildToolsVersion 的設定,因為3.0.1以上的gradle外掛會自動用一個預設的BuildTool版本,不需要像以前一樣,在每個build.gradle裡寫明buildToolsVersion了。 所以我們把提示到的各個build.gradle中的這行刪掉,再重新同步一下gradle,就不會報這個錯了。
-
再次同步gradle,繼續報錯:
Error:All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com/r/tools/flavorDimensions-missing-error-message.html
需要在app/build.gradle加上預設的dimension;大概就是為了保證各個渠道包要保持某些屬性的一致;總之按照官網上的說法,只要給每個渠道都設定一個“flavorDimensions”就可以了,程式碼如下(app/build.gradle)
flavorDimensions "default"//這個名字貌似隨便取,也可以有多個,總之一定要有.. productFlavors { market { dimension "default" } // other{ // dimension "default" //如果有其他的渠道,也要做類似的宣告 // } }
-
繼續同步,繼續報錯... 這次的錯誤提示比較接地氣了,說是build/intermediates/xxxx.xml 裡的某個值沒有找到,這個簡單,build目錄下的都是編譯期生成的檔案,clean下再來一發;或者直接rebuild專案(rebuild = clean + build)
-
rebuild專案,仍然報錯
Error:Execution failed for task ':framework:third:xxxModule:javaPreCompileDebug'. Annotation processors must be explicitly declared now. The following dependencies on the compile classpath are found to contain annotation processor. Please add them to the annotationProcessor configuration. xxxxxx.jar (com.xxxx:xxxxx:1.0.5) Alternatively, set android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true to continue with previous behavior. Note that this option is deprecated and will be removed in the future. See https://developer.android.com/r/tools/annotation-processor-error-message.html for more details.
一時半會兒不知道怎麼搞,看看這個開發者文件連結的說明吧, developer.android.com這個域名不翻牆是進不去的,不過現在已經有了國內的域名,把域名替換成developer.android.google.cn,後面保持不變就可以訪問了;其他類似的官方文件地址也一樣可以用這個方法去訪問。
看了下官方文件,大意是在說,工程裡的某個module依賴了某個jar包,然後jar裡面又用到了註解,在新的gradle版本里,需要寫新的groovy程式碼來對每個引用註解的地方單獨配置。在以往的版本中,gradle會預設給每個module都依賴一個annotationProcess,導致很多多餘的對annotationProcess的依賴,老版本中的預設方法,會在將來版本中被刪除。
按照提示,解決方案大致有兩個: 要麼我們需要在依賴於註解的module中,加上“annotationProcessor”這個配置; 要麼我們可以設定android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true,但是要注意這玩法以後會被刪除
按照官方推薦的第一種方法:應該是在報錯的build.gradle中修改:
dependencies { compile xxxxx ... //加上類似這些對於註解處理器的的依賴 annotationProcessor 'com.xxxx.xxxxx-1.0.0' }
但是我加上了並沒有什麼作用,原因待查.. 為了節約時間,還是先用includeCompileClasspath=true的辦法湊合下吧,以後真的被刪除了再說...
直接在app/build.gradle(準確的說是每個涉及到註解依賴的module的build.gradle)上加一行
defaultConfig { minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true //加上這行即可 } } }
然後再次同步gradle
-
同步gradle,不出意外的又報錯了。 一堆的style屬性未找到的問題,跟第六步裡的現象貌似是一樣的
Error:(713, 5) error: style attribute '@android:attr/windowExitAnimation' not found. Error:(713, 5) error: style attribute '@android:attr/windowExitAnimation' not found. Error:(713, 5) error: style attribute '@android:attr/windowExitAnimation' not found.
這才發現其實clean是沒有用的,真正的問題原因在一堆錯誤的最後幾行
Error:java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for details Error:java.util.concurrent.ExecutionException: com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for details
AAPT2 , 貌似就是aapt的2.0版本? aapt.exe 是 Android SDK裡的一個工具,詳情出門左轉自己查去.. 直接說解決方案:在Project/gradle.properties中新增 android.enableAapt2=false 再次同步...
-
嗯,沒錯,又報錯了。
Error:(247, 1) Execution failed for task ':app:processMarketDebugManifest'. Manifest Tasks does not support the manifestOutputFile property any more, please use the manifestOutputDirectory instead. For more information, please check https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html
這個錯跟7號有點類似,又是新版本gradle外掛不支援某些方法啦,又要換用新的寫法才行啦...然後最後給你貼一個文件地址自己看去。。 大概就是說:現在不支援manifestOutputFile這個方法,要用processManifest.manifestOutputDirectory()來替換,
也就是你app/build.gradle 打包的這一段程式碼要重新寫一下;一般我們都會在build.gradle中編寫這樣的程式碼,來實現對Manifest檔案的修改、以及自定義apk的輸出檔名等。
android.applicationVariants.all { variant -> variant.outputs.each { output -> output.processManifest.doLast { ... def manifestFile = output.processManifest.manifestOutputFile; //這裡被廢棄導致報錯 def apkFileName = "_myapp_${android.defaultConfig.versionCode}_${formatedDate}.apk"; manifestFile.write(updatedContent, 'UTF-8') ... } } }
-
改了之後還是報錯,還是這裡,連續各種錯
比如
Error:(250, 1) Execution failed for task ':app:processMarketDebugManifest'. No signature of method: java.lang.String.getText() is applicable for argument types: (java.lang.String) values: [UTF-8] Possible solutions: getAt(java.lang.String), getAt(groovy.lang.IntRange), getAt(groovy.lang.Range), getAt(int), getAt(java.util.Collection), getAt(groovy.lang.EmptyRange)
比如
Error:(251, 1) Execution failed for task ':app:processMarketDebugManifest'. No signature of method: java.lang.String.write() is applicable for argument types: (java.lang.String, java.lang.String) values: [H:\GitWorkSpace\MyClient\app\build\intermediates\manifests\full\market\debug/AndroidManifest.xml, ...] Possible solutions: wait(), trim(), size(), size(), toSet(), wait(long)
比如
Error:(258, 1) Execution failed for task ':app:processMarketDebugManifest'. Cannot set the value of read-only property 'outputFile' for ApkVariantOutputImpl_Decorated{apkData=Main{type=MAIN, fullName=marketDebug, filters=[]}} of type com.android.build.gradle.internal.api.ApkVariantOutputImpl.
反正就是各種各樣的groovy語法報錯,然而並不懂groovy語法,現學現賣改一改 我們工程裡是既有動態修改manifest檔案的需求,也有自定義apk名字的功能, 包括自動修改apk名稱的程式碼也有報錯,也要改,最終改成了這樣
android.applicationVariants.all { variant -> variant.outputs.each { output -> output.processManifest.doLast { def umengKey = "11111111" //取得manifest路徑 String manifestPath = "$manifestOutputDirectory/AndroidManifest.xml" //取得manifest的文字內容 def manifestContent = file(manifestPath).getText('UTF-8') //替換UMENG_APPKEY的文字 manifestContent = manifestContent.replaceAll("\\{\\{UMENG_APPKEY\\}\\}", umengKey) //重新把內容寫進.xml檔案裡 file(manifestPath).write(manifestContent, 'UTF-8') } } variant.outputs.all { //定義apk名字 def formatedDate = new Date().format("yyyyMMddHHmm") def apkFileName = "_myapp_${android.defaultConfig.versionCode}_${formatedDate}.apk"; outputFileName = apkFileName } }
-
至此再次同步gradle,總算是沒有再報錯了,AS上可以正常執行程式碼了。 我們的全部修改都只涉及到gradle的配置程式碼,所以不會對專案裡的業務邏輯產生任何影響。 這時候再打個包,驗證下我們的最後一段打包相關的gradle指令碼是否正常執行.打包成功了,也就全部OK了。