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 task打包 安卓gradle配置_android


一个项目有一个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定义的相同,后者会被覆盖。典型的 配置内容如下:
  • 其中:
  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 的编译:

  • 开启并行编译
org.gradle.parallel=true
  • 开启编译守护进程
org.gradle.daemon=true
  • 加大可用编译内存
org.gradle.jvmargs=-Xms256m -Xmx1024m

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

8. 自动优化无用的资源

在编译的时候,我们可能会有很多资源并没有用到,此时就可以通过shrinkResources来优化我们的资源文件,除去那些不必要的资源。

Android 配置gradle task打包 安卓gradle配置_常见问题_02


某些情况下,一些资源是需要通过动态加载的方式载入的,这时候我也需要像 Progard 一样对我们的资源进行keep操作。方法就是在res/raw/下建立一个keep.xml文件,通过如下方式 keep 资源:

Android 配置gradle task打包 安卓gradle配置_常见问题_03

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 task打包 安卓gradle配置_Gradle_04


添加日志信息:

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 task打包 安卓gradle配置_android_05

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文件的大小,如下:

Android 配置gradle task打包 安卓gradle配置_Android_06


可以看到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 <application> 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 <application> 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中找不到。
解决方法:

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关闭
  • 开始调试
    打好断点,在下图中的位置1处选择创建好的remote调试任务,并在点击位置2处开始调试

    调试界面如下(位置1处为创建的remote调试任务):

    遗留问题:gradle脚本在调试时虽然在断点处停止了,但是断点所在行却并不高亮,目前不知道原因

参考

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