自从将 Android 开发工具从 eclipse 迁移到 AndroidStudio 中之后,Android 工程构建越来越方便。见过很多项目中各式各样的 Gradle 配置语法,懵懵懂懂。遂花费近两周时间将官方文档通读一遍并整理出常用 Gradle 配置 Demo,以此博客作为说明。
1. 准备工作
一个最新版本的 AndroidStudio 毋庸置疑。下载地址:请戳。此处使用 3.5
版本。
Gradle 版本 5.4.1
,无须单独下载,在工程中配置好即可。
2. 基本概念
2.1. Module
软件工程中,通常采用模块化开发的策略,将一个项目划分成若干部分,多人协作开发中,每人完成一部分,最终完成整个项目。因此 AndroidStudio 也采用模块化管理的方式编译 Android 工程 。各个模块之间的依赖关系类似于树形结构,主 Module 作为根节点,其余多个库 Module 都直接或简介的被主 Module 引用。
2.2. Project
基于前面的描述,Project 即 AndroidStudio 打开的一个目录,其中包含 N+1 个 Module ,通过 Gradle 构建多个 Module 并最终合并成一个 apk 文件。
其实,在一个 Project 目录中,可以有多个主 Module ,每个主 Module 可以依赖相同的库 Module 并负责生成一个 apk 文件。通常不建议这么使用,因为这样会破坏 Module 之间的树形依赖关系,不利于后期维护。推荐将复用频率较高的库 Module 封装成 aar 文件,通过直接依赖或远程依赖的方式用于不同的主 Module 之中。
2.3. buildType & productFlavor
通过直译的方式 ,可以理解成 构建类型 和 产品特性 。构建类型中,默认包含 debug 和 release 两种类型。一些基本的差异如 :debug 包允许调试,release 包不允许调试。产品特性可以根据实际需要自行定义,如:收费和免费版本,汉语、英语及日语。不同的特性之间通过维度的概念区分。
最终如上图所示,生成 2x3x2 共 12 种不同的 apk 文件。
3. 工程结构
精简后的 AndroidStudio 工程结构如下:
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
| ├── app │ ├── build.gradle │ ├── proguard-project.txt │ └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ └── flueky │ │ └── demo │ │ └── MainActivity.java │ └── res │ ├── layout │ │ └── activity_main.xml │ └── values │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle
|
- 文件夹 app 作为一个 Module ,必须有 build.gradle 文件,src 文件夹包含全部源文件:Java 文件、资源文件、清单文件等。
- 文件 build.gradle 不同于 app/build.gradle , 它用于整个工程的配置,而 app/build.gradle 只作用于 app Module 的配置。
- gradle 包含使用的 gradle 插件版本 。若未指定 ,AndroidStudio 会自动生成最新的 gradle 插件目录。
- gradlew、gradlew.bat 分别是 Mac/Linux 和 Windows 的脚本文件。关联于 gradle 插件版本,不可单独删除。
- settings.gradle 包含整个工程的所有 Module 的声明。
settings.gradle 示例:
1 2 3 4 5 6 7 8
| include ':app'
include ':library'
include 'other-lib' project(':other-lib').projectDir = new File(rootDir, "../other-sample/library")
|
4. 配置 Project
4.1. 构建脚本
1 2 3 4 5 6 7 8 9 10 11 12 13
| buildscript { repositories { google() jcenter() mavenCentral() }
dependencies { classpath 'com.android.tools.build:gradle:3.4.1' } }
|
4.2. 全工程配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| allprojects { repositories { google() jcenter() mavenCentral()
flatDir { dirs 'libs' } } }
|
4.3. 扩展变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ext { applicationId = "com.flueky.demo" compileSdkVersion = 29 buildToolsVersion = "29.0.0" minSdkVersion = 19 targetSdkVersion = 29
app = [ versionCode: 1, versionName: '1.0.0' ] }
|
4.4. 加载配置文件
1 2
| apply from: "config.gradle"
|
5. 配置 Module
5.1. 加载插件
1 2 3 4 5 6
| apply plugin: 'com.android.application'
apply plugin: 'com.android.library'
apply plugin: 'java-library'
|
还有很多 Plugin 可以用,如: java
web
maven-publish
等。
另一种写法,一个 Module 中使用多个插件,如:
1 2 3 4 5
| plugins { id 'java' id 'war' id 'maven-publish' }
|
5.2. 使用SDK
1 2 3 4 5 6
| android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion }
|
rootProject.ext 是在 Project 的 build.gradle 文件中声明。
5.3. 默认配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| android{ defaultConfig { applicationId rootProject.ext.applicationId minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode rootProject.ext.app.versionCode versionName rootProject.ext.app.versionName ndk{ abiFilters 'armeabi-v7a' } resConfigs "zh-rCN" multiDexEnabled true } }
|
5.4. 签名配置
签名文件,至少配置一个。如未使用,debug 包,会使用 user home/.android/debug.keystore
的签名文件。 release 包默认不签名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| def ksPropFile = rootProject.file("keystore.properties") def ksProp = new Properties()
ksProp.load(new FileInputStream(ksPropFile))
android{ signingConfigs { release { keyAlias ksProp['keyAlias'] keyPassword ksProp['keyPassword'] storeFile file(ksProp['storeFile']) storePassword ksProp['storePassword'] } debug { keyAlias 'flueky' keyPassword 'android' storeFile file('../demo.keystore') storePassword 'android' } } }
|
使用配置的好处是,将生产签名文件信息保存在配置文件中,不添加到版本控制工具中,可以有效防范签名文件信息泄露,被他人使用。
针对签名文件的校验,会有专门的防篡改技术,防止应用被反编译 apk 后二次打包。如签名文件泄露,会构成很大威胁。
5.5. 构建类型
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
| android{ buildTypes { debug { debuggable true jniDebuggable true signingConfig signingConfigs.debug versionNameSuffix = '-beta' applicationIdSuffix '.debug' } release { debuggable false jniDebuggable false signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt' minifyEnabled true shrinkResources true
} inner { initWith debug applicationIdSuffix '.inner' } } }
|
AndroidStudio 默认支持 debug 和 release 两种类型。并有一些默认的配置。常用配置见上。
5.6. 产品特性
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
| android{ flavorDimensions 'stage', 'api'
productFlavors { dev { dimension 'stage' } pro { dimension 'stage' }
minApi21 { dimension 'api' minSdkVersion 21 } minApi23 { dimension 'api' minSdkVersion 23 } minApi26 { dimension 'api' minSdkVersion 26 } } }
|
产品特性和构建类型,不仅仅是在构建的时候修改些配置,还可以在 src 目录下,定义同名的文件夹,存放 java 、res 、assets 和 AndroidManifest.xml 等。用于实现不同的业务逻辑,资源图片等。
5.7. 过滤变体
产品特性和构建类型可以通过组合的方式生成多个 apk 文件。但在实际中可能不需要部分组合方式。忽略后,可加速编译过程。
如,内部使用不需要生产阶段的 apk 文件,配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| android{ variantFilter { variant -> def names = variant.flavors*.name def type = variant.buildType.name if (names.contains('pro') && type.equals("inner")) { setIgnore(true) } } }
|
5.8. 细分APK
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| android{ splits {
abi { enable true universalApk true exclude 'x86', 'x86_64' }
density { enable true reset() include "xhdpi"
} } }
|
universalApk 只用在 abi 的配置中。true 表示按照默认的方式将全部 so 打包 。density 中,此值必须为 true。
使用 abi 和 density 要注意同 ndk.abiFilters 和 resConfigs 使用的冲突。
5.9. 添加依赖
依赖第三方库,在新版本 Gradle 中,常用的有 implementation
和 api
。
它们区别是:
- A 中 implementation B , B 中 implementation C 。 B 可以使用 C 的类和方法,A 可以使用 B 的类和方法。
- A 中 implementation B , B 中 api C 。 B 可以使用 C 的类和方法,A 可以使用 B 的类和方法。A 还可以使用 C 的类和方法。
这样的好处是,如果只修改了 Module C , 情况 1 只会重新编译 B 和 C ,情况 2 会重新编译 A B C 。 因此在实际应用中,避免大量使用 api
的方式。
下面列举了对 jar 文件、 aar 文件和 maven 库文件的依赖方式。
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
| dependencies { implementation fileTree(include: '*.jar', dir: 'libs') implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: []) debugImplementation fileTree(include: '*.jar', dir: 'src/debug/libs') releaseImplementation fileTree(include: '*.jar', dir: 'src/release/libs')
implementation project(':library') implementation project(':other-lib')
implementation 'com.android.support:appcompat-v7:28.0.0' implementation('com.android.support:support-v4:28.0.0') { exclude group: 'com.android.support', module: 'collections' }
implementation 'com.google.dagger:dagger:2.24' annotationProcessor 'com.google.dagger:dagger-compiler:2.24' implementation 'androidx.multidex:multidex:2.0.1' }
|
注意到上面的配置中存在 debugImplementation 和 releaseImplementation 。这是对不同构建类型单独指定的依赖方式。
产品特性也可以使用上面的配置,但是需要先声明,方式如下:
1 2 3 4 5 6 7
| configurations { devMinApi21DebugImplementation {} devMinApi21Api {} devDebugCompileOnly {} minApi21DebugRuntimeOnly {} }
|
篇幅有限 ,以上介绍均是常用配置。更多配置请见源码。
- 修改生成的 apk 文件目录。
- 修改生成的 apk 文件名称。
- 动态修改版本号。
- 替换 build 缓存目录。
- AndroidManifest.xml 注入变量。
关于 AndroidManifest.xml 合并冲突,有机会在后面的文章中讲。
6. 常见问题
6.1. 问题1
主 Module 有 inner 的构建类型,库 Module 中、没有 ,构建 app 时会报错。
可在库 Module 中添加 inner 的构建类型,或者在主 Module 的 inner 类型中,添加下面的配置。
1 2
| matchingFallbacks = ['inner', 'debug', 'release']
|
如需匹配 release 时,将 release 放在 debug 前,或者直接删除 debug 。
6.2. 问题2
主 Module 有的产品特性,库 Module 中没有,构建 app 时会报错。
错误原因同 问题 1
。如,库 Module 中存在其他类似的产品特性,可使用匹配的方式 ,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| android{ productFlavor{ minApi21 { minSdkVersion 21 // 库 module 没有 minApi21 ,匹配较高版本 // 同时需要在 AndroidManifest.xml 中使用 overrideLibrary matchingFallbacks = ['minApi23'] } minApi26 { minSdkVersion 26 // 库 module 没有 minApi26 ,匹配较低版本,可兼容 matchingFallbacks = ['minApi23'] } } }
|
如,库 Module 中没有定义任何产品特性,可以直接在 defauleConfig
中,忽略对维度的依赖 。
1 2 3 4 5 6
| android{ defauleConfig{ missingDimensionStrategy 'dev', 'pro' } }
|
6.3. 问题3
主 Module 的 minSdk 小于 库 Module 的 minSdk。
使用 overrideLibrary ,指定复写库 Module 的 packageName 。
1 2 3 4
| <manifest xmlns:tools="http://schemas.android.com/tools"> <uses-sdk tools:overrideLibrary="com.flueky.library" /> </manifest>
|
源码地址。
觉得有用?那打赏一个呗。[去打赏](/donate/)