Android开发中常用Gradle配置及问题处理-1(截止2020.3.16)

Android开发中常用Gradle配置

    • 1. Gradle文件结构
    • 2. AAR引用
    • 3. 自动生成versionCode,versionName
    • 4. 配置release和debug的applicationId
    • 5. 保持依赖版本同步
    • 6. 编译成library
    • 7. 优化编译速度
    • 8. 自动优化无用的资源
    • 9. Gradle依赖配置compile,implementation,api和classpath的区别
    • 10. Gradle log
    • 11. FAQ
      • 11.1 Gradle sync failed: 'xxx' already disposed:
      • 11.2 调试时出现“line number info is not available”
      • 11.3 Compilation error. See log for more details通用解决方法
      • 11.4 Android dependency * has different version for the compile (2.0.1) and runtime (3.0.3) classpath.
      • 11.5 error in opening zip file
      • 11.6 gradle定位manifest的问题
      • 11.7 增加gradle的超时以获得maven依赖
      • 11.8 提示找不到submodule依赖的问题
    • 12. 如何调试gradle plugin
      • 12.1 打印堆栈信息
      • 12.2 加log
      • 12.3 调试gradle
    • 参考

该博客是我在使用Android Gradle过程中遇到的问题和学习汇总,博客中的内容主要是参考其他博客。所参考博客的地址都写在参考一节了。

1. Gradle文件结构

对于一个gradle 项目,最基础的文件配置如下:
Android开发中常用Gradle配置及问题处理-1(截止2020.3.16)_第1张图片
一个项目有一个setting.gradle、包括一个顶层的 build.gradle文件、每个Module (如app)都有自己的一个build.gradle文件。

  • setting.gradle:这个 setting 文件定义了哪些module 应该被加入到编译过程,对于单个module 的项目可以不用需要这个文件,但是对于 multimodule 的项目我们就需要这个文件,否则gradle 不知道要加载哪些项目。这个文件的代码在初始化阶段就会被执行。如:
  • ``` include ':app' include ':csc' ```
  • 顶层的build.gradle: 顶层的build.gradle文件的配置最终会被应用到所有项目中。它典型的配置如下
  • buildscript {
        repositories {
            jcenter()
        }
    
        dependencies {
            classpath 'com.android.tools.build:gradle:1.2.3'
        }
    }
    
    allprojects{
        repositories{
            jcenter()
        }
    }
    

    其中:

    1. buildscript:定义了 Android 编译工具的类路径。repositories中,jcenter是一个著Maven 仓库;
    2. allprojects:中定义的属性会被应用到所有 moudle 中,但是为了保证每个项目的独立性,我们一般不会在这里面操作太多共有的东西。
  • 每个项目单独的 build.gradle:针对每个moudle 的配置,如果这里的定义的选项和顶层build.gradle定义的相同,后者会被覆盖。典型的 配置内容如下:每个项目单独的 build.gradle:针对每个moudle 的配置,如果这里的定义的选项和顶层build.gradle定义的相同,后者会被覆盖。典型的 配置内容如下:
  • Android开发中常用Gradle配置及问题处理-1(截止2020.3.16)_第2张图片
    Android开发中常用Gradle配置及问题处理-1(截止2020.3.16)_第3张图片
    其中:

    1. apply plugin:第一行代码应用了Android 程序的gradle插件,作为Android 的应用程序,这一步是必须的,因为plugin中提供了Android 编译、测试、打包等等的所有task。
    2. android:这是编译文件中最大的代码块,关于android 的所有特殊配置都在这里,这就是我们前面的声明的 plugin 提供的。
    3. defaultConfig:程序的默认配置,注意,如果在AndroidMainfest.xml里面定义了与这里相同的属性,会以这里的为主。
      注意:applicationId的选项:在我们曾经定义的AndroidManifest.xml中,那里定义的包名有两个用途:一个是作为程序的唯一识别ID,防止在同一手机装两个一样的程序;另一个就是作为我们R资源类的包名。在以前我们修改这个ID会导致所有用引用R资源类的地方都要修改。但是现在我们如果修改applicationId只会修改当前程序的ID,而不会去修改源码中资源文件的引用。
    4. buildTypes:定义了编译类型,针对每个类型我们可以有不同的编译配置,不同的编译配置对应的有不同的编译命令。默认的有debug、release 的类型。
    5. dependencies:是属于gradle 的依赖配置。它定义了当前项目需要依赖的其他库。

    2. AAR引用

    协作开发中引用AAR是比较正常的事情,这一条属于基本技能。引用也是十分简单的,直接把aar文件放到libs目录下,然后在gradle下面配置一行:

    repositories{
        flatDir{
            dirs 'libs'
        }
    }
    

    注意:外层的repositories不能少,然后直接就可以在dependencies中添加引用,注意单引号

    compile(name: 'widget2-debug', ext: 'aar')
    

    3. 自动生成versionCode,versionName

    直接依靠gradle的语法来实现版本号的修改,分离主版本子版本,根据自己的逻辑进行修改。

    def versionMajor = 2
    def versionMinor = 1
    def versionPatch = 0
    
    android {
        ...
        defaultConfig {
        ...
            versionCode versionMajor * 100 + versionMinor * 10 + versionPatch
            versionName "${versionMajor}.${versionMinor}.${versionPatch}"
        }
    }
    

    4. 配置release和debug的applicationId

    使用applicationidsuffix可以为debug版本添加一个后缀到你的applicationid上。这个小技巧可以让我们在同一台设备上安装多个版本的apk,不用频繁卸载。

    buildTypes {    
        debug {
            applicationIdSuffix ".debug"
        }
        ...
    }
    

    5. 保持依赖版本同步

    在现在越来越大的项目中,各种Module,子Module,引用版本不一致导致经常需要同步下载,而且一旦想要升级插件,support包之类的,可能有好多个需要同时升级。使用Gradle,我们也能做到这一点。首先在最外层的根build.gradle中,在ext块内定义一个版本

    ext {
        supportLibVer = "24.2.1"
    }
    

    这样我们就能在需要的地方引用了

    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'libs')
        compile "com.android.support:appcompat-v7:$rootProject.ext.supportLibVer"
        compile "com.android.support:design:$rootProject.ext.supportLibVer"
        ...
    }
    

    包版本可以这么玩,其他的所有配置其实都可以这么玩,我们可以把配置抽成一个单独的gradle文件,里面就一个ext block。

    ext {
       androidPluginVer = "2.1.3"
       compileSdkVer = 24
       buildToolsVer = "24.0.2"
    
       minSdkVer = 14
       targetSdkVer = 24
    
       supportLibVer = "24.2.1"
    }
    

    我们把这个命名成config.gradle,然后要做的就是让子module都能引用到这个,简单的做法就是直接让刚才的root gradle文件引用这个gradle配置文件。
    这里有一点坑就是看你自己创建的gradle目录在哪一级,需要定位到才能找得到 不然会出错,这里我是在最外层,所以加了两处,另外改ext属性,需要手动同步一下,直接跑的话并不会自动同步,这是最关键的一点。

    buildscript {
        apply from: 'config.gradle'
        ...
    }
    
    subprojects {
        apply from: '../config.gradle'
    }
    

    使用的话和前面的ext属性差不多,加不加rootProject都可以跑得动

    6. 编译成library

    如果我们要将一个module编译成library让其他的项目引用,我们需要在该module的bubild.gradle中将plugin设置为如下plugin:

    apply plugin: 'com.android.library'
    

    7. 优化编译速度

    可以通过以下方式加快gradle 的编译:

  • 开启并行编译
  • 在项目根目录下面的 gradle.properties中设置
    org.gradle.parallel=true
    
  • 开启编译守护进程
  • 该进程在第一次启动后回一直存在,当你进行二次编译的时候,可以重用该进程。同样是在gradle.properties中设置
    org.gradle.daemon=true
    
  • 加大可用编译内存
  • 同样是在gradle.properties中设置
    org.gradle.jvmargs=-Xms256m -Xmx1024m
    

    引用的时候在setting文件中include即可。

    8. 自动优化无用的资源

    在编译的时候,我们可能会有很多资源并没有用到,此时就可以通过shrinkResources来优化我们的资源文件,除去那些不必要的资源。
    Android开发中常用Gradle配置及问题处理-1(截止2020.3.16)_第4张图片
    某些情况下,一些资源是需要通过动态加载的方式载入的,这时候我也需要像 Progard 一样对我们的资源进行keep操作。方法就是在res/raw/下建立一个keep.xml文件,通过如下方式 keep 资源:
    在这里插入图片描述

    9. Gradle依赖配置compile,implementation,api和classpath的区别

    Gradle3.4之前的版本使用如下方式添加依赖:

    dependencies {
    compile 'commons-httpclient:commons-httpclient:3.1'
    compile 'org.apache.commons:commons-lang3:3.5'
    }
    

    Gradle 3.4+后使用如下方式添加依赖:

    dependencies {
    api 'commons-httpclient:commons-httpclient:3.1'
    implementation 'org.apache.commons:commons-lang3:3.5'
    }
    

    其中api和implementation两种依赖的不同点在于:它们声明的依赖其他模块是否能使用。

    • api:当其他模块依赖于此模块时,此模块使用api声明的依赖包是可以被其他模块使用
    • implementation:当其他模块依赖此模块时,此模块使用implementation声明的依赖包只限于模块内部使用,不允许其他模块使用。

    classpath一般是添加 buildscript 本身需要运行的东西,buildscript是用来加载gradle脚本自身需要使用的资源,可以声明的资源包括依赖项、第三方插件、maven仓库地址等,classpath 声明的依赖,不会编译到最终的 apk 里面。

    buildscript {
        repositories {
            google()
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:2.3.3'
            classpath 'com.jakewharton:butterknife-gradle-plugin:8.2.1'
        }
    }
    

    10. Gradle log

    Gradle log级别如下:

    public enum LogLevel {
        DEBUG,
        INFO,
        LIFECYCLE,
        WARN,
        QUIET,
        ERROR
    }
    

    Gradle log输出开关选项:

    开关选项 输出的日志级别
    -d, --debug DEBUG及更高级别(全部日志)
    -i, --info INFO及更高级别
    -q, --quiet Log errors only
    -w, --warn WARN及更高级别

    Gradle提供的日志接口:
    Android开发中常用Gradle配置及问题处理-1(截止2020.3.16)_第5张图片
    添加日志信息:

    logger.quiet('quiet 日志')
    logger.error('error 日志')
    logger.warn('warn 日志')
    logger.lifecycle('lifecycle 日志')
    logger.info('info 日志')
    logger.debug('debug 日志')
    

    11. FAQ

    11.1 Gradle sync failed: ‘xxx’ already disposed:

    错误特征:

    14:37   Gradle sync started
    14:37   Gradle sync failed: 'shadows-core-3.0' already disposed:
    14:37   Gradle sync completed
    14:39   Gradle sync started
    14:39   Gradle sync failed: 'robolectric-resources-3.0' already disposed:
    14:40   Gradle sync completed
    

    解决方法:
    ./gradlew clean
    Restart Android stduio

    11.2 调试时出现“line number info is not available”

    编译debug版本时打开了混淆开关,按如下方式修改:

    buildTypes {
            debug {
                testCoverageEnabled = true
                debuggable true
                minifyEnabled false //值改为false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.debug
            }
            ......
     }
    

    11.3 Compilation error. See log for more details通用解决方法

    以Android Studio IDE为例说明:
    在Android Studio中打开项目,在其Terminal窗口中执行如下命令:

    ./gradlew build --stacktrace  2>logerr.txt
    或者
    ./gradlew build --stacktrace >logs.txt  2>logerr.txt //不输入任何编译信息
    

    然后进入项目的根目录中查看logerr.txt,可以发现第一行即为出错的位置,如下图:
    Android开发中常用Gradle配置及问题处理-1(截止2020.3.16)_第6张图片

    11.4 Android dependency * has different version for the compile (2.0.1) and runtime (3.0.3) classpath.

    出现该问题时其实已经提示了解决方法“You should manually set the same version via DependencyResolution.”,具体解决方法是在根目录的build.gradle中添加如下代码:

    subprojects {
        project.configurations.all {
            resolutionStrategy.eachDependency { details ->
                if (details.requested.group == 'com.tbuonomo.andrui'
                        && !details.requested.name.contains('multidex') ) {
                    details.useVersion '23.1.1' //指定要使用的版本号
                }
            }
        }
    }
    

    11.5 error in opening zip file

    出现这个问题一般是因为解压gradle zip(如gradle-4.6-all.zip)解压失败,可以进入到.gradle的目录中查看一下gradle zip文件的大小,如下:
    在这里插入图片描述
    可以看到gradle-4.6-all.zip只有64K,正常情况下会有100多M。可以从如下几个方面解决问题:

    • 网络是否有问题
    • 查看gradle-wrapper.properties中的distributionUrl属性是否正确,一般正确的如是:https://services.gradle.org/distributions/gradle-4.6-all.zip

    11.6 gradle定位manifest的问题

    如下面问题:

    * What went wrong:
    Execution failed for task ':app:processDevDebugManifest'.
    > Manifest merger failed : Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory) from [com.android.support:support-compat:28.0.0] AndroidManifest.xml:22:18-91
            is also present at [androidx.core:core:1.0.0] AndroidManifest.xml:22:18-86 value=(androidx.core.app.CoreComponentFactory).
            Suggestion: add 'tools:replace="android:appComponentFactory"' to  element at AndroidManifest.xml:17:5-70:19 to override.
    

    可以使用如下gradle命令来定位:

    ./gradlew :app:processDevDebugManifest --stacktrace 或 ./gradlew :app:processDebugManifest --stacktrace
    

    定位出的信息如下:

    /Users/**/project name/app/src/main/AndroidManifest.xml:22:18-91 Error:
            Attribute application@appComponentFactory value=(android.support.v4.app.CoreComponentFactory) from [com.android.support:support-compat:28.0.0] AndroidManifest.xml:22:18-91
            is also present at [androidx.core:core:1.0.0] AndroidManifest.xml:22:18-86 value=(androidx.core.app.CoreComponentFactory).
            Suggestion: add 'tools:replace="android:appComponentFactory"' to  element at AndroidManifest.xml:17:5-70:19 to override.
    
    See http://g.co/androidstudio/manifest-merger for more information about the manifest merger.
    

    11.7 增加gradle的超时以获得maven依赖

    ./gradlew build -Dhttp.socketTimeout=60000 -Dhttp.connectionTimeout=60000
    

    默认http.socketTimeout为10s,http.connectionTimeout为30s

    如果您使用的是gradle 4.6或更高版本,请使用以下属性:
    ./gradlew build -Dorg.gradle.internal.http.socketTimeout=60000 -Dorg.gradle.internal.http.connectionTimeout=60000

    11.8 提示找不到submodule依赖的问题

    log:

    Unable to resolve dependency for ':app@xxPreview/compileClasspath': Could not resolve project :library.
    Could not resolve project :library.
    Required by: project :app
    > Unable to find a matching configuration of project :library:
             - Configuration 'debugApiElements':
                 - Required com.android.build.api.attributes.BuildTypeAttr 'preview' and found incompatible value 'debug'.
                 - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Aar' and found compatible value 'Aar'.
                 - Found com.android.build.gradle.internal.dependency.VariantAttr 'debug' but was't required.
                 - Required org.gradle.api.attributes.Usage 'java-api' and found compatible value 'java-api'.
                 - Required versionCode 'xiaomi' but no value provided.
             - Configuration 'debugRuntimeElements':
                 - Required com.android.build.api.attributes.BuildTypeAttr 'preview' and found incompatible value 'debug'.
                 - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Aar' and found compatible value 'Aar'.
                 - Found com.android.build.gradle.internal.dependency.VariantAttr 'debug' but wasn't required.
                 - Required org.gradle.api.attributes.Usage 'java-api' and found incompatible value 'java-runtime'.
                 - Required versionCode 'xiaomi' but no value provided.
             - Configuration 'releaseApiElements':
                 - Required com.android.build.api.attributes.BuildTypeAttr 'preview' and found incompatible value 'release'.
                 - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Aar' and found compatible value 'Aar'.
                 - Found com.android.build.gradle.internal.dependency.VariantAttr 'release' but wasn't required.
                 - Required org.gradle.api.attributes.Usage 'java-api' and found compatible value 'java-api'.
                 - Required versionCode 'xiaomi' but no value provided.
             - Configuration 'releaseRuntimeElements':
                 - Required com.android.build.api.attributes.BuildTypeAttr 'preview' and found incompatible value 'release'.
                 - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Aar' and found compatible value 'Aar'.
                 - Found com.android.build.gradle.internal.dependency.VariantAttr 'release' but wasn't required.
                 - Required org.gradle.api.attributes.Usage 'java-api' and found incompatible value 'java-runtime'.
                 - Required versionCode 'xiaomi' but no value provided.
    

    原因:app中buildTypes集合不是library的buildTypes集合子集,即app中buildType属性preview在依赖的library中找不到。
    解决方法:
    https://juejin.im/entry/5b17bce0e51d45067b059980

    12. 如何调试gradle plugin

    12.1 打印堆栈信息

    在gradle命令行加上–stacktrace,报错后会打印出堆栈信息

    12.2 加log

    请参考第10节

    12.3 调试gradle

    • 开启调试的守护进程
      打开Terminal窗口,在当前的工程目录下,输入 :
    gradlew assembleDebug -Dorg.gradle.daemon=false -Dorg.gradle.debug=true
    

    gradlew assembleDebug  -Dorg.gradle.debug=true --no-demo
    
    • 创建remote调试任务:
      选择 Run -> Eidt Configurations,点左上角的 + 号,选择 remote。Name可以随意命名,其他配置可以不用动,端口就5005,点ok关闭
      Android开发中常用Gradle配置及问题处理-1(截止2020.3.16)_第7张图片
    • 开始调试
      打好断点,在下图中的位置1处选择创建好的remote调试任务,并在点击位置2处开始调试
      在这里插入图片描述
      调试界面如下(位置1处为创建的remote调试任务):
      Android开发中常用Gradle配置及问题处理-1(截止2020.3.16)_第8张图片
      遗留问题:gradle脚本在调试时虽然在断点处停止了,但是断点所在行却并不高亮,目前不知道原因

    参考

    1. Android开发中那些常用的Gradle配置. https://www.jianshu.com/p/d412e04ab38d
    2. Android Gradle 完整指南. https://www.cnblogs.com/laughingQing/p/5855774.html
    3. https://www.jianshu.com/p/1eac2ecacf88

    你可能感兴趣的:(gradle)