文章出自:点击打开链接
Android工程中的Gradle
下面简述对我们工程最重要的几个Gradle文件,后续也会围绕他们进行详细讲解和补充
(请仔细看代码中的注释哈)
- 工程Project 中的
build.gradle
: 工程控制Gradle编译配置 - 模块module中的
build.gradle
: 控制每个Module的编译过程 gradle.properties
: gradle动态参数的配置文件local.properties
: 本地的配置,如:SDK位置gradle-wrapper.properties
:gradle本地代理,声明了指向目录和版本distributionUrl
: 指定gradle版本不存在时,就从Value的地址中去下载。很多时候,我们只要版本换成我们本地存在的gradle版本就可以了
settings.gradle
: 配置Gradle中的Module管理
动态参数配置
在Gradle中动态配置资源参数
我们可以根据各自的需求在不同的领域(如:buildType 的debug, defaultConfig ...)下去动态替换或配置我们项目中所使用到的资源,如 log 开关, 针对不同渠道的对应内容字段,不同版本定义引入的不同值等等
首先说清一点,对于动态资源在build.gradle
中多个领域中的使用,会遵循一下顺序来进行覆盖:
buildType > productFlavor > defaultConfig > Manifest中的配置 > 依赖的第三方库的配置 > 任意领域中的默认值(也就是没有设置值)
Manifest
占位符: 可以动态配置Manifest的参数
在Manifest的Application节点下
//这里以友盟为例
<!-- 友盟统计相关meta-data -->
<meta-data
android:name="UMENG_APPKEY"
android:value="balabalabala" />
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL_VALUE}" />
在build.gradle中对参数进行动态配置
productFlavors {
baidu {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
}
}
gradle.properties
的使用- 系统属性:
配置:systemProp.proName=123
使用:System.properties['proName']
- 自定义属性 :
配置: 有key:ray.proName=123
,无key :proName = 123
使用: 有key:project。property('proName')
, 无key :proName
- 系统属性:
gradle.properties中的配置
#使用系统参数配置
systemProp.keyStore=ray.jks
#使用key/value键值对配置
ray.keyPassword=123456
#属性配置
mKeyAlias=ray
build.gradle中的使用
//签名打包
release {
//签名文件所在路径
storeFile file(System.properties['keyStore'])
//签名密码
storePassword "111111"
//别名
keyAlias mKeyAlias
keyPassword project.property('ray.keyPassword')
}
BuildConfig
文件- 在
app/build/generated/source/buildConfig
文件夹下面 - 使用:
buildConfigField "String" , "key" , "\"value\""
配置buildConfig
中的属性参数String
: 参数类型(int,boolean...),key
: 属性的名字,value
: 属性的值, `` 为转义字符
- 在
resValue
动态修改工程资源- 和buildConfig 类似,
resValue("string","key","value")
- 其中
string
表示 会在app/build/generated/res/resValue/.../generated.xml
中生成对应的String 的 key 和Value, 代码中可以直接getResources().getString(R.string.key);
获取到value - 注意: 因为Gradle编译的时候会将脚本配置和string.xml文件中配置进行merge,所以string.xml中已经存在的key在编译前要删除,否则会报错
- 和buildConfig 类似,
//下面模拟在不同渠道下修改资源参数
productFlavors{
baidu{
buildConfigField "String" , "productCode" , "\"baidu 1.0\""
resValue("string","productName","baidu")
}
}
Project : build.gradle
整体结构与描述
//声明引入的参数配置文件
apply from: 'config/dependencies.gradle'
apply from: 'config/text.gradle'
//编译配置
buildscript {
//自定义参数
ext {
kotlin_version = '1.1.0'
compile_version = 25
}
//Gradle指定使用jcenter代码仓库
repositories {
jcenter()
}
//声明依赖Android Gradle 插件版本
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
//这里可以为项目整体配置属性
allprojects{
repositories {
jcenter()
}
}
//任务:每次构建的时候删除指定目录
task clean(type: Delete) {
delete rootProject.buildDir
}
Module : build.gradle
控制每个Module的编译过程以及具体的参数配置
defaultConfig
基本配置信息
//默认配置
defaultConfig {
//包名
applicationId "com.rayhahah.gradledemo"
//最低版本
minSdkVersion 19
//目标版本
targetSdkVersion 25
//版本代码
versionCode getVersinCode()
//版本
versionName "1.0"
//自动化测试
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
resValue "int","test","1"
}
signConfigs
签名配置信息
signingConfigs {
//debug模式签名文件
debug {}
//签名打包
release {
//签名文件所在路径
storeFile file("ray.jks")
//签名密码
storePassword "111111"
//别名
keyAlias "rayhahah"
keyPassword "111111"
}
//自定义签名配置
ray{
//和上面的属性一致,根据个人需求实现不同配置
}
}
buildTypes
编译类型 : 指定编译不同类型情况下的不同配置信息
这里是列举了部分属性和方法,全部的方法和属性请看官网文档
//构建配置
buildTypes {
release {
//是否启用资源优化
minifyEnabled
//启用舍弃无用资源,只有当开启混淆才能够启用
shrinkResources false
//指定混淆文件
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//指定我们release包的输出文件名就是我们的渠道名字
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith(".apk")) {
def fileName = "${variant.productFlavors[0].name}" + ".apk"
output.outputFile = new File(outputFile.parent, fileName);
}
}
}
}
debug {
}
//继承
// rayhahah.initWith(debug)
//自定义buildType
rayhahah {
//指定签名配置文件
signingConfig signingConfigs.debug
//包名增加后缀
applicationIdSuffix ".ray"
}
}
sourceSets
配置资源逻辑组
- 通过指定资源 文件夹路径构建自己的工程目录
指定Android所需要文件夹所在具体路径
sourceSets {
//这样的配置适用于将Eclipse中的项目结构迁移到AndroidStudio中
main {
//指定src资源目标目录
java.srcDirs = ['src']
//指定asset的目标目录
assets.srcDirs = ['assets']
//指定res的目标目录
res.srcDirs = ['res']
//指定依赖C文件的目标目录
jni.srcDirs = ['jni']
//指定依赖so文件的目标目录
jniLibs.srcDirs = ['libs']
//指定Manifest的目标文件路径
manifest.srcFile 'AndroidManifest.xml'
}
}
- 常用! 给自己的layout分包管理!!
- 可以参考博文
1. 在res下新建文件夹 layouts(其实叫什么都无所谓)
2. 然后在 layouts下 新建你要分的包 如: activity,fragment 或按照业务模块来分
3. 在分包内新建Android resource directory -> layout 不要改名字
4. 在module:build gradle 如下配置
5. 然后将以前的layout文件拷贝到对应分包的layout下就可以使用了
PS:只有在Project目录才能看到,Android目录结构是看不到的
sourceSets {
main {
res.srcDirs = [
'src/main/res/layouts/activity',
'src/main/res/layouts/fragment',
'src/main/res/layouts',
'src/main/res'
]
}
}
productFlavors
打包渠道配置信息(仔细看代码注释)
//多渠道打包配置
//利用Manifest占位符动态参数配置
productFlavors {
baidu {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
}
wandoujia {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
}
googleplayer {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "googleplayer"]
}
//不想每一个都去配置渠道名称也可以用下面这个函数
//这个函数可以将 Manifest中的占位符替换成 每个渠道的名字
productFlavors.all {
flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}
}
compileOptions
//编译配置项
//主要配置Java编译版本
compileOptions {
sourceCompatibility org.gradle.api.JavaVersion.VERSION_1_8
targetCompatibility org.gradle.api.JavaVersion.VERSION_1_8
}
lintOptions
//lint配置项
lintOptions {
//启用出错停止grgradle构建
abortOnError false
// true--检查所有问题点,包含其他默认关闭项
checkAllWarnings true
// 关闭指定问题检查
disable 'TypographyFractions','TypographyQuotes'
// 打开指定问题检查
enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
// 仅检查指定问题
check 'NewApi', 'InlinedApi'
// true--生成HTML报告(带问题解释,源码位置,等)
htmlReport true
// html报告可选路径(构建器默认是lint-results.html )
htmlOutput file("lint-report.html")
// 忽略指定问题的规则(同关闭检查)
ignore 'TypographyQuotes'
}
build.gradle 图形化配置
AndroidStuido 给我们提供了十分友好地对于Build.gradle 图形化配置的界面,使用如下:
- File -> Project Structure -> 选择需要配置的 Module —> 选择需要配置的领域
如图十分直观:
![](https://i-blog.csdnimg.cn/blog_migrate/5ea5ab49c951d77baf447d252336c42a.webp?x-image-process=image/format,png)
dependencies 项目依赖
- build.gradle中dependencies中的配置解析
//dependencies : 当前Android Module构建过程中所依赖的所有库
dependencies {
//依赖指定目录下所有指定后缀的文件
compile fileTree(dir: 'libs', include: ['*.jar'])
//测试工具依赖
testCompile 'junit:junit:4.12'
//远程库的依赖 (当从远程库中下载一次过后,就会缓存到本地了)
//默认远程库配置为 jcenter()
compile 'com.android.support:appcompat-v7:25.2.0'
//依赖指定文件(这里依赖的是jar包)
compile file('libs/test-1.0.0.jar')
//依赖本地项目库
compile project(':testLibrary')
//格式: groupId: com.squareup.retrofit2
// artifactId : retrofit
// version: 2.1.0
// SNAPSHOT : 表示依赖 retrofit 及其依赖的所有项目,如果他所依赖的项目在本项目中重复出现依赖,则只依赖retrofit项目中的。
// @aar : 表示只依赖retrofit,不依赖他所依赖的项目
compile ('com.squareup.retrofit2:retrofit:2.1.0-SNAPSHOT@aar')
{
//强制刷新远程库,避免远程库刷新,本地未更新
transitive = true
//exclude : 单独去除okhttp3的依赖
exclude module : 'com.squareup.okhttp3:okhttp:3.3.0'
}
}
-
so库依赖
- 在
src/main
目录下创建jniLibs
,然后将so文件拷贝进去就可以了 - 当然也可以通过
sourceSet
指定jniLib
的目标目录来自定义管理依赖的so文件存放
- 在
-
本地Module依赖
- 在
build.gradle
的dependencies
领域中添加compile project(':testLibrary')
- 在
setting.gradle
中添加module到include
中 如:include ':app',':testLibrary'
- 在
方法def定义
在Gradle中你可以写方法供 配置信息动态调用
//自定义函数
def getVersinCode() {
// ......
}
Android{
defaultConfig{
versionCode getVersinCode()
}
}
Gradle编译提速优化
检测
gradlew build -profile
: 编译工程同时生成编译性能分析文件,在根目录build/reports/profile/profile-xxxx.xxx....html
,通过浏览器打开以后
如图: 我们需要优化的就是Task Execution
![](https://i-blog.csdnimg.cn/blog_migrate/95911945049f0cb4b408ed3caba4003d.webp?x-image-process=image/format,png)
- 往下拉,可以看到TaskExcution的详细参数
![](https://i-blog.csdnimg.cn/blog_migrate/e0f53cb736dfaa77132e5da9a949b4cb.webp?x-image-process=image/format,png)
可以看到lint耗时最多,然后我们就可以根据自己项目中的具体情况来做优化
禁用Task达到提速
- 根据上面的耗时来对应禁用Task来达到提速的效果
- 在
Project:build.gradle
中的buildScript
中动态配置编译时禁用即可, 代码:gradle.startParameter.excludedTaskNames.add('lint')
就可以实现禁用了,具体需要继续禁用的可以根据项目输出的编译分析文件来作出添加和调整
AAPT
aapt即Android Asset Packaging Tool,在SDK的build-tools目录下。该工具可以查看,创建, 更新ZIP格式的文档附件(zip, jar, apk)。也可将资源文件编译成二进制文件,尽管你可能没有直接使用过aapt工具,但是build scripts和IDE插件会使用这个工具打包apk文件构成一个Android 应用程序(百度百科)
- 在Debug模式下,我们需要优化AAPT来大量提速我们的编译(记得在release下改回来)
aaptOtions{
cruncherEnabled = false
}
Gradle编译优化
- 提升Gradle本身编译速度
gradle.properties
中配置
//开启守护线程支持
org.gradle.daemon=true
//开启并行编译
org.gradle.parallel=true
//按需编译
org.gradle.configureondemand=true
//手动配置Gradle编译时内存分配
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# 开启JNI编译支持过时API
android.useDeprecatedNdk=true
build.gradle
中配置
//增加编译内存到4g
dexOptions{
incremental true
javaMaxHeapSize "4g"
}
添加依赖于你的build.gradle支持MultiDex库
dependencies { ... compile 'com.android.support:multidex:' ... }
第2步
在buildType或productFlavor中开启multiDexEnabled。
defaultConfig { ... multiDexEnabled true ... }
现在,根据你的项目情况,你有3种选择:
如果你没有创建自己的Application 类,在你的清单文件AndroidManifest.xml中配置 android.support.multidex.MultiDexApplication就可以了。
.... android:name="android.support.multidex.MultiDexApplication" ...
如果你有自己的Application类了,让它继承 android.support.multidex.MultiDexApplication而不是 android.app.Application
public class MyApplication extends FooApplication { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } }
不论你选择上面哪种,都会创建多个大小差不多的dex文件代替单个庞大的dex文件。运行的时候回同事加载所有的这些dex文件。