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
- 参考
该博客是我在使用Android Gradle过程中遇到的问题和学习汇总,博客中的内容主要是参考其他博客。所参考博客的地址都写在参考一节了。
1. Gradle文件结构
对于一个gradle 项目,最基础的文件配置如下:
一个项目有一个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() } }
其中:
- buildscript:定义了 Android 编译工具的类路径。repositories中,jcenter是一个著Maven 仓库;
- allprojects:中定义的属性会被应用到所有 moudle 中,但是为了保证每个项目的独立性,我们一般不会在这里面操作太多共有的东西。
- 每个项目单独的 build.gradle:针对每个moudle 的配置,如果这里的定义的选项和顶层build.gradle定义的相同,后者会被覆盖。典型的 配置内容如下:每个项目单独的 build.gradle:针对每个moudle 的配置,如果这里的定义的选项和顶层build.gradle定义的相同,后者会被覆盖。典型的 配置内容如下:
其中:- apply plugin:第一行代码应用了Android 程序的gradle插件,作为Android 的应用程序,这一步是必须的,因为plugin中提供了Android 编译、测试、打包等等的所有task。
- android:这是编译文件中最大的代码块,关于android 的所有特殊配置都在这里,这就是我们前面的声明的 plugin 提供的。
- defaultConfig:程序的默认配置,注意,如果在AndroidMainfest.xml里面定义了与这里相同的属性,会以这里的为主。
注意:applicationId的选项:在我们曾经定义的AndroidManifest.xml中,那里定义的包名有两个用途:一个是作为程序的唯一识别ID,防止在同一手机装两个一样的程序;另一个就是作为我们R资源类的包名。在以前我们修改这个ID会导致所有用引用R资源类的地方都要修改。但是现在我们如果修改applicationId只会修改当前程序的ID,而不会去修改源码中资源文件的引用。 - buildTypes:定义了编译类型,针对每个类型我们可以有不同的编译配置,不同的编译配置对应的有不同的编译命令。默认的有debug、release 的类型。
- 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来优化我们的资源文件,除去那些不必要的资源。
某些情况下,一些资源是需要通过动态加载的方式载入的,这时候我也需要像 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提供的日志接口:
添加日志信息: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 stduio11.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,可以发现第一行即为出错的位置,如下图:
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 <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=6000011.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/5b17bce0e51d45067b05998012. 如何调试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脚本在调试时虽然在断点处停止了,但是断点所在行却并不高亮,目前不知道原因
参考
- Android开发中那些常用的Gradle配置. https://www.jianshu.com/p/d412e04ab38d
- Android Gradle 完整指南. https://www.cnblogs.com/laughingQing/p/5855774.html
- https://www.jianshu.com/p/1eac2ecacf88