Android Gradle的理解以及升级Gradle插件3.0.1遇到的坑

以下内容均为个人理解,如果有描述不正确的地方,欢迎指正
转载请注明原文链接

Gradle简介

开始填坑之前,先简单聊聊gradle,Gradle其实就是一个构建项目的工具,也就是把那一个个文件、文件夹按照一定的规则关联起来,形成一个项目的工具,它其实不仅仅是用在AndroidStudio上。

我们在AS中用到的Gradle其实应该被叫做 Android Gradle Plugin,也就是安卓项目上的gradle插件;
Gradle插件会有版本号,每个版本号又对应有一个或一些 Gradle发行版本(一般是限定一个最低版本),也就是我们常见的类似gradle-3.1-all.zip这种东西;

如果这两个版本对应不上了,那你的工程构建的时候就会报错。
对应关系如下(参考自 https://developer.android.google.cn):

插件版本 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的踩坑之旅

  1. 把工程目录下的build.gradle中,将gradle插件版本升级到3.0.1

     dependencies {
         classpath 'com.android.tools.build:gradle:3.0.1' //这里从2.2.2改到了3.0.1
     }
    
  2. 点击同步gradle,报错。
    提示gradle-wrapper版本过低,gradle-wrapper的作用,就是设置你的项目工程要对应用那个Gradle发行版本来执行构建,
    3.0.1的插件版本必须对应4.1以上的gradle版本,因此需要在../gradle/wrapper/gradle-wrapper.properties文件中把版本号改为4.1

     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即可;

  3. 重启自动同步之后,又报错。
    提示有一些依赖库无法正常引用,需要添加google maven 仓库的依赖
    在工程目录下的build.gradle 文件加上maven依赖即可,

     repositories {
         mavenCentral()
         jcenter()
    
         //加上下面这段
         maven {
             url 'https://maven.google.com/'
             name 'Google'
         }
     }
    
  4. 再同步gradle,又报错。
    这次是提示没有26.0.2版本的 SDK buildTool ,然后直接在AS报错弹框里点击下载就可以了。现在SDK可以不用翻墙直接下载,速度还蛮快的。下载确认解压之后再次同步gradle。

  5. 同步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,就不会报这个错了。

  6. 再次同步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"  //如果有其他的渠道,也要做类似的声明
             //        }
         }
    
  7. 继续同步,继续报错...
    这次的错误提示比较接地气了,说是build/intermediates/xxxx.xml 里的某个值没有找到,这个简单,build目录下的都是编译期生成的文件,clean下再来一发;或者直接rebuild项目(rebuild = clean + build)

  8. 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

  9. 同步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
    再次同步...

  10. 嗯,没错,又报错了。

    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')
                ...
            }
        }
    }
    
  11. 改了之后还是报错,还是这里,连续各种错

    比如

        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
        }
    }
    
  12. 至此再次同步gradle,总算是没有再报错了,AS上可以正常运行代码了。
    我们的全部修改都只涉及到gradle的配置代码,所以不会对项目里的业务逻辑产生任何影响。
    这时候再打个包,验证下我们的最后一段打包相关的gradle脚本是否正常运行.打包成功了,也就全部OK了。

参考文献

谷歌爸爸的文档:
https://developer.android.google.cn/studio/build/gradle-plugin-3-0-0-migration.html

最后

附一张图

折腾一下午之后的我.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容