Gradle在Android中的自定义用法

参考链接:

《Android Developer》
《Android Plugin DSL》

使用共享库

在Android中除了标准的SDK还存在两种库:

一种是add-ons库(位于sdk的add-ons目录下,大部分是第三方厂商或者公司开发的)
一种是optional库(位于platforms/android-xx/optional目录下,一般是为了兼容旧版本的API)

第一类库Android Gradle会自动解析,帮我们添加到classpath中。
第二类库需要我们自己手动添加到classpath中。

1
2
3
android {
useLibrary 'org.apache.http.legacy'
}

最好在AndroidManifest文件里面配置一下uses-library标签,类似于google Maps如下:

1
2
3
<uses-library
android:name="com.google.android.maps"
android:required="true"/>

批量生成apk文件名

Android Gradle为我们提供了三个属性:applicationVariants、libraryVariants、testVariants.
这三个属性都返回DomainObjects对象集合,里面的元素分别是ApplicationVariant、LibraryVariants和TestVariants.修改它们会自动触发创建所有任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

android{

//....

applicationVariants.all { variant ->
variant.outputs.each { output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')
&& 'release'.equals(variant.buildType.name)){
def flavorName = variant.flavorName.startWith("_") ? variant.flavorName.
substring(1) : variant.flavorName
def apkFile = new File(output.outputFile.getParent(),
"Project_${flavorName}_v${variant.versionName}_${buildTime()}.apk")
}
}
}

def buildTime(){
def date = new Date()
def formattedDate = date.format("yyMMdd")
return formattedDate
}
}

applicationVariants中的variant都是ApplicationVariant,通过查看源码,可以看到它有一个outputs作为它的输出。

动态生成版本信息

version.gradle

1
2
3
4
ext{
appVersionCode=1
appVersionName="1.0.0"
}

build.gradle

1
2
3
4
5
6
7
8
9
apply from 'version.gradle'

android{

defaultConfig{
versionCode appVersionCode
versionName appVersionName
}
}

隐藏签名文件信息

对于apk的签名文件以及密码,我们存放到版本库和服务器都不是很好,所以下面将这些信息配置到本地环境变量再获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
android{
compileSdkVersion 27
buildToolsVersion '28.0.3'

signingConfigs {

def appStoreFile = System.getenv("STORE_FILE")
def appStorePassword = System.getenv("STORE_PASSWORD")
def appKeyAlias = System.getenv("KEY_ALIAS")
def appKeyPassword = System.getenv("KEY_PASSWORD")

release {
storeFile file(appStoreFile)
storePassword appStorePassword
keyAlias appKeyAlias
keyPassword appKeyPassword
}
}
}
}

动态配置AndroidManifest文件

1
<meta-data android:name="CHANNEL_NAME" android:value="${CHANNEL_VALUE}" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
android{

productFlavors{

google{

}

baidu{

}
}

productFlavors.all{ flavor ->
manifestPlaceholders.put("CHANNEL_NAME", name)
}
}

Android Gradle提供的manifestPlaceholders占位符方式,可以替换Manifest文件中任意${valu}格式的占位符。

自定义BuildConfig

BuildConfig类是Android工程自动生成的,不允许修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Automatically generated file. DO NOT MODIFY
*/
package com.parkingwang.business;

public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.parkingwang.business";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "Appium";
public static final int VERSION_CODE = 54001;
public static final String VERSION_NAME = "5.4.1";
// Fields from product flavor: Appium
public static final boolean DISABLE_KEYBOARD = true;
public static final boolean USING_TEST_API = true;
// Fields from default config.
}

可以看到这里面有一些配置信息,是否debug、appid、版本号、版本名称等,如果有所变化这些配置信息都会自动修改的。

Android Gradle提供了buildConfigField(String type, String name, String value)让我们自己添加到BuildConfig中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Adds a new field to the generated BuildConfig class.
*
* <p>The field is generated as: {@code <type> <name> = <value>;}
*
* <p>This means each of these must have valid Java content. If the type is a String, then the
* value should include quotes.
*
* @param type the type of the field
* @param name the name of the field
* @param value the value of the field
*/
public void buildConfigField(
@NonNull String type, @NonNull String name, @NonNull String value){ //... }

这样我们就可以使用这个特点结合productFlavors来配置不同渠道下的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
android{

productFlavors{

google{
buildConfigField 'String', 'WEB_URL', 'http://www.google.com'
}

baidu{
buildConfigField 'String', 'WEB_URL', 'http://www.baidu.com'
}
}
}

利用这个特点我们就可以不同的渠道做一些特殊的处理。

动态自定义资源文件

res/values类型的资源文件可以在Android Gradle中定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
android{
compileSdkVersion 27
buildToolsVersion '28.0.3'

productFlavors{

google{
resValue 'string', 'channel_tips', 'google渠道'
}
baidu{
resValue 'string', 'channel_tips', 'baidu渠道'
}
}
}

这样我们在工程中使用channel_tips字符串的时候就会相应的使用对应渠道的字符串内容。

除了string资源之外,还可以使用 id、bool、dimen、integer、color等资源。

Java编译选项

有的时候我们需要配置JDK的版本来使用一些新的特性,Android Gradle给我们提供了一种方便的配置方式。

1
2
3
4
5
6
7
8
9
10
android{
compileSdkVersion 27
buildToolsVersion '28.0.3'

compileOptions{
encoding = 'utf-8'
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}

adb操作配置

adbOptions有两个属性:installOptions和timeOutInMs

1
2
3
4
5
6
7
8
9
android{
compileSdkVersion 27
buildToolsVersion '28.0.3'

adbOptions{
timeOutInMs = 5 * 1000 //秒
installOptions = '-r', '-s'
}
}

timeOutInMs是设置adb命令的超时时间,单位为毫秒。
installOptions是adb install 的可选参数。

参数 含义
-l 锁定该应用程序
-r 替换已存在的应用程序,强制安装
-t 允许测试包
-s 把应用程序安装到SD卡
-d 允许进行降级安装
-g 为该应用程序授予所有运行时权限

DEX选项配置

dx命令是一个脚本,调用的是dx.jar,默认情况下给dx分配1024M内存。Android Gradle提供了dexOptions{ }对dx操作进行一些配置。

1
2
3
4
5
6
7
8
android{
compileSdkVersion 27
buildToolsVersion '28.0.3'

dexOptions{
incremental true
}
}

incremental 属性是dx的增量模式,速度更快一些,有很多限制可能会不工作,所以慎用。

javaMaxHeapSize 属性解决dx时的内存不足问题,可以配置一个内存大小。

1
2
3
dexOptions{
javaMaxHeapSize '4g'
}

jumboMode 属性用来配置是否开启jumbo模式,也就是65535问题(Android 5.0及以上可用)。

1
2
3
dexOptions{
jumboMode true
}

preDexLibraries 属性配置是否开启执行dex Libraries库工程。

threadCount属性配置dx运行时候的线程数量。