基础概念

Gradle

Gradle 是以类 java 语法的 Groovy 语言为基础,基 于JVM 的自动化构建工具。一般通过编写 build.gradle 脚本实现编译构建逻辑开发。

Gradle Wrapper

Gradle Wrapper 是 Gradle 的封装,一个 Gradle Wrapper 对应一个特定版本的 Gradle。gradlew 为 Gradle Wrapper 的缩写。当用户第一次执行 gradlew 命令时,Gradle Wrapper 会自动下载、安装对应版本的 Gradle。

Android Gradle Plugin

Android Gradle Plugin 简写 AGP,是基于 Gradle 的 Android 应用的构建系统。与 Gradle 版本存在对应关系。

Plugin version Gradle version
1.0.0 - 1.1.3 2.2.1 - 2.3
1.2.0 - 1.3.1 2.2.1 - 2.9
1.5.0 2.2.1 - 2.13
2.0.0 - 2.1.2 2.10 - 2.13
2.1.3 - 2.2.3 2.14.1 - 3.5
2.3.0+ 3.3+
3.0.0+ 4.1+
3.1.0+ 4.4+
3.2.0 - 3.2.1 4.6+
3.3.0 - 3.3.3 4.10.1+
3.4.0 - 3.4.3 5.1.1+
3.5.0 - 3.5.4 5.4.1+
3.6.0 - 3.6.4 5.6.4+
4.0.0+ 6.1.1+
4.1.0+ 6.5+
4.2.0+ 6.7.1+
7.0 7.0+

Android Studio 配置

Android Studio 中一般来说主要关注四个配置文件:gradle.properties、local.properties、gradle-wrapper.properties、settings.gradle、build.gradle。

1
2
3
4
5
6
7
8
9
10
11
12
13
MyApp
├── build.gradle // 仓库设置、gradle 插件版本设置
├── settings.gradle // 项目模块及依赖设置
├── gradle.properties // gradle 的全局配置文件。
├── local.properties // gradle 的本地配置文件。
├── gradlew
├── gradlew.bat
├── gradle
| └── wrapper
| ├── gradle-wrapper.jar
| └── gradle-wrapper.properties // gradle 版本及位置设置
└── module
└── build.gradle

gradle.properties

gradle.properties 是 Gradle 的全局性配置文件,主要有两方面用途:

  1. 可以在此文件中自定义全局的属性,然后在项目中其他的 *.gradle 里面直接引用。
  2. 配置全局 gradle、system、project 的属性和环境变量,如代理服务、JAVA_HOME、是否使用 androidx。

自定义配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 用于 gradle 直接使用配置示例
COMPILE_SDK_VERSION=28
TARGET_SDK_VERSION=28

SUPPORT_APPCOMPAT_V7_VERSION=28.0.0

# 用于 java 代码使用配置示例
DEFAULT_NAME=xiaoming
DEFAULT_NUMBER=10086

# 用于 xml 文件调用配置示例
USER_NAME=xioahong
TEXT_SIZE=20sp
TEXT_COLOR=#ef5350
  • 在 *.gradle 中使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
android {
compileSdkVersion COMPILE_SDK_VERSION as int
defaultConfig {
targetSdkVersion TARGET_SDK_VERSION as int
...
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

// for java 代码调用
buildConfigField("String", "defaultName", "\"${DEFAULT_NAME}\"")
buildConfigField("Integer", "defaultNumber", DEFAULT_NUMBER)

// for xml 布局文件调用
resValue("string", "user_name", "${USER_NAME}")
resValue("dimen", "text_size", "${TEXT_SIZE}")
resValue("color", "text_color", "${TEXT_COLOR}")

...
}
}
...
}

dependencies {
...
implementation "com.android.support:appcompat-v7:${SUPPORT_APPCOMPAT_V7_VERSION}"
...
}
  • 在 *.java 中使用:
1
2
System.out.println("defaultName:" + BuildConfig.defaultName);
System.out.println("defaultNumber:" + BuildConfig.defaultNumber);
  • 在 *.xml 中使用:
1
2
3
4
5
6
7
8
9
10
11
12
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/user_name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textSize="@dimen/text_size"
android:textColor="@color/text_color"
/>

注意:

  1. 在 gradle.properties 中定义的属性默认是 String 类型的,如果需要 int 类型,使用时需要添加 as int 后缀。
  2. 使用时,直接引用变量名即可。在字符串中使用时,要添加 ${}
  3. 如果要在 java 代码中使用,一般先将其配置为 BuildConfig。然后在代码中调用。
  4. 如果要在 xml 中使用,一般先将其配置为 resValue。然后在 xml 中引用 @string。

代理配置

1
2
3
4
5
6
7
8
9
10
11
12
13
// http 配置
systemProp.http.proxyHost=www.somehost.org
systemProp.http.proxyPort=8080
systemProp.http.proxyUser=userid
systemProp.http.proxyPassword=password
systemProp.http.nonProxyHosts=*.nonproxyrepos.com|localhost

// https 配置
systemProp.https.proxyHost=www.somehost.org
systemProp.https.proxyPort=8080
systemProp.https.proxyUser=userid
systemProp.https.proxyPassword=password
systemProp.http.nonProxyHosts=*.nonproxyrepos.com|localhost

也可以在 IDE 的 Settings –> Appearance & Behavior –> System Settings –> HTTP Proxy 选项下进行配置。

local.properties

local.properties 与 gradle.properties 类似,但不会被添加到版本控制中,因此主要用来配置一些本地的变量和本地的 SDK 目录。

本地 SDK 配置

1
2
3
// sdk 配置
sdk.dir=/Users/xxxx/Library/Android/sdk
ndk.dir=/Users/xxxx/Library/Android/ndk/android-ndk-r19c

AGP 4.1 以上版本,通过 build.gradle 文件中使用 android.ndkVersion 和 android.ndkPath 属性指定相应的版本。

本地自定义配置

1
2
3
4
5
// 本地自定义变量
key.file=C\:\\work\\Key.jks
keyAlias=key
keyPassword=key7766
storePassword=key6677
  • 在 *.gradle 中使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

//加载资源
Properties properties = new Properties()
InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream();
properties.load( inputStream )

//读取文件
def sdkDir = properties.getProperty('key.file')
storeFile file( sdkDir )

//读取字段
def key_keyAlias = properties.getProperty( 'keyAlias' )
def key_keyPassword = properties.getProperty( 'keyPassword' ) ;
def key_storePassword = properties.getProperty( 'storePassword' ) ;

storePassword key_storePassword
keyAlias key_keyAlias
keyPassword key_keyPassword

gradle-wrapper.properties

Android Studio 新建工程时,会在项目根目录自动生成 Wrapper 所需的目录及文件。

1
2
3
4
5
6
|____gradle
| |____wrapper
| | |____gradle-wrapper.jar //具体业务逻辑
| | |____gradle-wrapper.properties //配置文件
|____gradlew //Linux 下可执行脚本
|____gradlew.bat //Windows 下可执行脚本

其中,gradle-wrapper.properties 是 gradle-wrapper 的配置文件,主要用于配置项目使用的 gradle 版本及下载解压路径。

1
2
3
4
5
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
  • distributionUrl:下载的gradle的地址。
    • gradle-xx-all.zip是完整版,,包含了各种二进制文件,源代码文件,和离线的文档。
    • gradle-xx-bin.zip是二进制版,只包含了二进制文件(可执行文件)。
    • gradle-xx-src.zip是源码版,只包含了Gradle源代码,不能用来编译工程。
  • zipStoreBase 和 zipStorePath 组合在一起,是下载的 gradle-xx-xxx.zip 所存放的位置。
  • distributionBase 和 distributionPath 组合在一起,是下载的 gradle-xx-xxx.zip 解压后的位置。
目录 示例
GRADLE_USER_HOME 用户目录 在 windows 下是 %USERPROFILE%/.gradle,例如 C:\Users\\.gradle\。在 linux 下是$HOME/.gradle,例如~/.gradle.
PROJECT 工程的当前目录 gradlew 所在的目录

settings.gradle

项目中可能存在多份 settings.gradle。其中项目根目录的 settings.gradle 文件包含项目的模块等配置。

1
2
3
4
5
include ':module1', ':module2'

// 设置 module 路径 (一般同一项目路径下,无需设置)
project(':module1').projectDir = new File("module1")
project(':module2').projectDir = new File("module2")

build.gradle

项目中可能存在多份 build.gradle。其中项目根目录的 build.gradle 文件包含项目依赖(Java 或Android library)源仓库及 gradle 插件版本信息。

  1. repositories 闭包,声明了依赖源仓库的配置;
  2. dependencies 闭包,声明了一个 Gradle 插件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
buildscript {
repositories { //repositories闭包
google() // 在 gradle4.1 之后添加,用于引用google自有的仓库。
jcenter() // 自2022年2月1日之后,JCenter将停止提供库的下载服务。
}
dependencies { //dependencies闭包
classpath 'com.android.tools.build:gradle:3.0.0' //声明 gradle 插件版本号为3.0.0
}
}

allprojects {
repositories {
google()
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

仓库配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 原生
repositories {
google() // 在gradle4.1之后,添加了新的语法google(),用于引用google自有的仓库。
jcenter() // 自2022年2月1日之后,JCenter将停止提供库的下载服务。
mavenCentral() // 用于替代 JCenter。
mavenLocal() // 使用本地 maven 仓库。
}

// 阿里云
repositories {
maven {url 'https://maven.aliyun.com/repository/google'}
maven {url 'https://maven.aliyun.com/repository/public'}
maven {url 'https://maven.aliyun.com/repository/central'}
}

使用 mavenLocal() 时 Gradle 默认会按以下顺序去查找本地的 maven 仓库:USER_HOME/.m2/settings.xml >> M2_HOME/conf/settings.xml >> USER_HOME/.m2/repository。因此,环境变量要加入 M2_HOME。另外,如果本地没有相关 jar 包,gradle 会下载到 USER_HOME/.gradle 文件夹下,若想让 gradle 下载到指定文件夹,配置 GRADLE_USER_HOME 环境变量,用于指定目录。

仓库的修改,也可以在 IDE 的 Module Setting –> project 选项下,配置 repositories。

gradle 插件版本配置

1
2
3
4
5
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
}
}

注意,gradle 插件版本和 gradle 版本配置时,必须满足对应关系!

gradle 插件版本的修改,也可以通过 File > Project Structure > Project 来配置。

Native 编译设置

一般在模块中,需要使用到 c++ 代码编译,可以选择 cmake 或者 ndk-build 两种构建工具,并根据选择配置模块的 build.gradle 文件。

cmake 构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 在模块的 build.gradle 中添加 cmake 构建工具
android {
...

defaultConfig {
...
externalNativeBuild {
cmake {
...
// Passes optional arguments to CMake.
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

// 指定cFlags 和 cppFlags
cFlags "-D__STDC_FORMAT_MACROS"
cppFlags "-fexceptions", "-frtti"

// 指定编译的架构(可省略,使用 ndk.abiFilters )
abiFilters "arm64-v8a", "armeabi-v7a"
}
}

ndk {
...
// 指定编译、打包的架构
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
'arm64-v8a'
}
}

externalNativeBuild {
cmake {
...
//指定 cmakelists 路径,相对与 module 跟目录
path "CMakeLists.txt"
version "cmake-version"
}
}

// 指定 ndk 版本与路径(一般不需要配置)
ndkPath "/Users/ndkPath/ndk21" // Point to your own NDK
ndkVersion "major.minor.build" // e.g., ndkVersion "21.3.6528147"
}

ndk-build 构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 在模块的 build.gradle 中添加 cmake 构建工具
android {
...

defaultConfig {
...
externalNativeBuild {
ndkBuild {
...
// 指定Application.mk
arguments "NDK_APPLICATION_MK:=src/main/jni/Application.mk"

// 指定cFlags 和 cppFlags
cFlags "-D__STDC_FORMAT_MACROS"
cppFlags "-fexceptions", "-frtti"

// 指定编译的架构(可省略,使用 ndk.abiFilters )
abiFilters "arm64-v8a", "armeabi-v7a"
}
}

ndk {
...
// 指定编译、打包的架构
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
'arm64-v8a'
}
}

externalNativeBuild {
ndkBuild {
...
// 指定 Android.mk
path "src/main/jni/Android.mk"
}
}

// 指定 ndk 版本与路径(一般不需要配置)
ndkPath "/Users/ndkPath/ndk21" // Point to your own NDK
ndkVersion "major.minor.build" // e.g., ndkVersion "21.3.6528147"
}

android 依赖配置

android-support

一般在模块中,使用到 android support 依赖时,需要配置模块的 build.gradle 文件,添加对应的版本。

1
2
3
4
5
6
7
8
9
// 在模块的 build.gradle 中添加 support 依赖版本
dependencies {
...
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:multidex:1.0.0'
implementation 'com.android.support:support-v13:28.0.0'
implementation 'com.android.support:preference-v14:28.0.0'
}

注意:

  • SDK 最高支持 support 库的版本为 28,因此项目的 compileSdkVersion 不能大于 28。

androidx

AndroidX 对原始 Android 支持库进行了重大改进,自 android sdk 29 开始完全取代了 support 库。

使用或者迁移到 AndroidX,需要在项目的 gradle.properties 中进行相关配置:

1
2
3
4
5
// 是否使用 AndroidX。(未指定时默认为 false)
android.useAndroidX=true

// 是否自动重写二进制文件来迁移现有的第三方库以使用 AndroidX。(未指定默认为 false)
android.enableJetifier=true

注意:

  • SDK 最低支持 androidx 库的版本为 29,因此项目的 compileSdkVersion 不能低于 29。
  • 迁移之前,应先将项目更新为使用支持库的最终版本:28.0.0。

使用 Android Studio 3.2 及更高版本,也可以从菜单栏中依次选择 Refactor > Migrate to AndroidX 进行迁移。

编译传参

1
2
# 通过命令行传参(-P)
gradlew :module:task -PYOUR_KEY=YOUR_VALUE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 在 build.config 中使用参数
android {
def yourKey = project.hasProperty('YOUR_KEY') ? YOUR_KEY : "defaultValue"

defaultConfig {
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0.0"

// 配置 buildConfig
buildConfigField("String", "buildConfig_Key", "\"${yourKey}\"")
}
}

编译构建问题

Build Tools 版本不匹配

【问题】:Error:failed to find Build Tools revision xx.x.x

【分析】:工程配置的Android BuildToolsVersion与本机安装的SDK Tool版本不一致。

【解决方法】:

  1. 修改工程各个 module的build.gradle 中 BuildToolsVersion 版本与本机安装的版本一致。
  2. 查看工程 settings.gradle 中的依赖项目,确保其 BuildToolsVersion 版本与本机安装的版本一致。

找不到android方法

【问题】:Could not find method android() for arguments [xxxxxxxxx] on root project ‘‘.

【分析】:不能在工程根目录的 build.gradle 中配置android block。

【解决方法】:将工程根目录build.gradle 中的android配置移动到module/build.gradle。

1
2
3
4
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
}

打包aar,lib 库冲突

【问题】:通过 Android Studio 打包的 aar,包含 lib 内的依赖 jar 包,导致使用时产生冲突。

【分析】:Gradle 支持6种编译方式。compile、provided、apk、test compile、debug compile 和 release compile。

  • compile:对所有 buildType 以及 flavors 进行编译并打包到apk 。
  • provided:和 compile 相似,但只在编译时使用,不打包到最终apk 。
  • apk:只会打包到 apk 中,不参与编译,所以不能在项目代码中使用相应库中的方法。
  • test compile:仅针对单元测试的代码编译打包。
  • debug compile:仅针对 debug 模式编译打包。
  • release compile:仅针对 release 模式编译打包。

【解决方法】:将 module 下的 build.gradle 中编译配置由 compile 改为 provided。

1
2
3
4
dependencies {
//compile fileTree(include: ['*.jar'], dir: 'libs')
provided fileTree(include: ['*.jar'], dir: 'libs')
}

BuildConfig 冲突

【解决方法】:关闭不必要的 BuildConfig 生成。

1
2
3
4
5
// 在 build.gradle 中添加以下配置
afterEvaluate {
generateReleaseBuildConfig.enabled = false
generateDebugBuildConfig.enabled = false
}

参考文档

https://developer.aliyun.com/mvn/guide?spm=a2c6h.13651104.0.0.435836a4edt4OQ
https://juejin.cn/post/6844903987557171213
https://docs.gradle.org/current/userguide/build_environment.html
https://developer.android.com/studio/projects/install-ndk?hl=zh-cn
https://www.cnblogs.com/steffen/p/9212765.html