Gradle
Gradle是Android项目的主流编译工具,是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。
编译周期
在解析Gradle的编译过程中会涉及到Gradle非常重要的两个对象:Project和Task。
每个项目编译至少有一个Project,其中一个build.gradle就代表一个Project,而每个Project又包含多个Task,其中Task又包含多个Action,而Action是一个代码块,里面包含需要被执行的代码,它们的包含关系如下:
Project > Task > Action
在编译过程中,Gradle会根据Build相关文件来聚合所有的Project和Task,并执行Task中的Action。
所有的Task的执行都按照一定的逻辑顺序,这种逻辑称之为依赖逻辑。
编译过程分为三个阶段:
初始化阶段:创建Project对象,如果有多个build.gradle则会创建多个Project。
配置阶段:会执行所有的编译脚本,同时还会创建Project中的所有Task,为下个阶段做准备。
执行阶段:Gradle会根据传入的参数决定执行这些Task,真正的Action执行代码就在这里。
Gradle结构
Android项目中的Gradle最基础的文件配置如下:
MyApp
├─ build.gradle
├─ settings.gradle
└─app
└─ build.gradle
一个项目包含一个settings.gradle、包括一个顶层的build.gradle文件,每个Module都有各自的build.gradle文件。
setting.gradle
setting.gradle
中的定义的是哪些Module应该被加入到编译过程,对于单Module项目可以不用该文件。对于多Module项目则需要该文件来指示需要加载哪些Module,它的代码在初始化阶段就会被执行。
include ':app'
include ':Module1'
include ':Module2'
rootProject.name = "MyApp"
多个Module通过include标签进行标明,项目就会根据指定的Module进行加载操作。
Project - build.gradle
一般Android项目中包含两个build.gradle,Project最顶层的build.gradle文件的配置会被应用到所有项目中。
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.1"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
buildscript{}
定义了Android编译工具的类路径,其中repositories里是gradle脚本执行所需的依赖,分别是对应Maven
和插件。
allprojects{}
中定义的属性会被应用到所有的Module中,但是要保证项目的独立性,不要在这里操作过多共有的配置。
App - build.gradle
每个Module都有单独的build.gradle,可以针对不同的Module进行配置,如果这里的配置和顶层的build.gradle相同的话,后者将会覆盖前者。
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.legend.demo"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
......
}
plugins{}
指定了Android程序的Gradle插件,plugin中提供了Android编译、测试和打包的所有Task。在早期的Gradle版本中不是使用id
的方式指定插件的,而是通过如下方式(两种都可以使用):
apply plugin: 'com.android.application'
android{}
是编译文件中最大的代码块,关于Android所有的特殊配置都在这里。其中'defaultConfig{}'就是程序的默认配置,如果在AndroidMainfest.xml
文件定义相同的属性的话,会以这里的为准。
buildTypes{}
定义了编译类型,针对不同的类型可以有不同的配置和编译命令。默认的有debug和release选项。
buildTypes{
// 发布类型
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
// 测试类型,给测试人员
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
// 本地类型,和后端联调使用
local {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
增加这些配置后,会在Android Studio的Build Variants面板中看到debug
和local
两个构建类型,点击运行即可构建。
Gradle Wrapper
Gradle不断的更新版本,新版本会对以往项目存在向后兼容的问题,所以Gradle Wrapper就应运而生。
在Android Studio构建的项目中会自带gradle-wrapper.jar文件,它还拥有一个配置文件:Project/gradle/wrapper/gradle-wrapper.properties
,它的内容如下所示:
#Sun Jan 17 11:15:10 CST 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-6.5-bin.zip
distributionUrl
是要下载的gradle地址以及版本,gradle-wrapper会去wrapper/list目录下查找,如果没有对应版本的gradle采取下载,当然我们也可以手动下载Gradle版本放入相应目录即可。
全路径为C:Users<user_name>.gradlewrapperdistsgradle-x.x-bin<url-hash>
Gradle有三种类型的版本:
1、gradle-xx-all.zip是完整版,包含了各种二进制文件,源代码文件,和离线的文档。
2、gradle-xx-bin.zip是二进制版,只包含了二进制文件(可执行文件),没有文档和源代码。
3、gradle-xx-src.zip是源码版,只包含了Gradle源代码,不能用来编译你的工程。
如果是直接从eclipse 中的项目转换过来的,程序并不会自动创建wrapper脚本,我们需要手动创建。在命令行输入以下命令即可
gradle wrapper --gradle-version 2.4
它会自动构建相应的目录结构。
Gradle 命令
Gradle 会根据build 文件的配置生成不同的task,我们可以直接单独执行每一个task。
gradlew tasks:列出项目中所有的task。
以上命令可以在Android Studio的Terminal面板中使用,如果出现以下提示:
'.gradlew' 不是内部或外部命令,也不是可运行的程序或批处理文件。
出现这种情况则需要在Android Studio的Gradle面板下的build setup中执行wrapper即可解决。
Gradle命令的基本语法如下所示:
gradle [taskName...] [--option-name...]
gradlew是包装器,其中./gradlew、gradle是通用的。
命令待补充......
Android tasks
Gradle中有四个基本的task,Android继承它们并进行了自己的实现:
assemble:对所有的 buildType 生成 apk 包。
clean:移除所有的编译输出文件,比如apk
check:执行lint检测编译。
build:同时执行assemble和check命令
在实际项目中会根据不同的配置,会对这些task 设置不同的依赖。比如 默认的assmeble会依赖 assembleDebug 和assembleRelease,如果直接执行assmeble,最后会编译debug和release 的所有版本出来。我们运行的许多命令除了会输出到命令行,还会在build文件夹下生产一份运行报告。
BuildConfig
我们可以通过BuildConfig.DEBUG来判断当前版本是否是debug版本,以此输出只有在debug环境下才会执行的操作。这个类是有gradle根据配置文件生成的。其中Gradle使用的是Groovy语言,它是一种JVM语言,所以即使语法不同,它们最终都会生成JVM字节码文件。
在app/gradle.build中的android{} 下有一个buildTypes{}可以配置key-value的键值对,它的功能非常强大。
比如我们可以为debug何release两种环境定义不同的服务器地址,比如:
android {
......
buildTypes{
release {
buildConfigField "String", "API_URL", ""http://10.0.0.1/api/""
buildConfigField "Boolean", "LOG_HTTP_CALLS", "true"
......
}
debug {
buildConfigField "String", "API_URL", ""http://legend.com/api/""
buildConfigField "Boolean", "LOG_HTTP_CALLS", "false"
......
}
}
......
}
除此之外,我们还可以为不同编译类型来设置不同的资源文件,比如:
android {
......
buildTypes{
release {
resValue "string", "app_name", "Example"
......
}
debug {
resValue "string", "app_name", "Example DEBUG"
......
}
}
......
}
然后在BuildConfig.java文件中会生成相应的静态变量,直接引用即可。
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.legend.demo";
public static final String BUILD_TYPE = "debug";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
// Field from build type: debug
public static final String API_URL = "http://legend.com/api/";
// Field from build type: debug
public static final Boolean LOG_HTTP_CALLS = false;
}
共享变量
在Android开发过程中,一个项目会有多个Module,如果想保持所有的Module和主Module的配置保持相同的话,可以在这里配置,具体步骤如下:
1、在Project的build.gradle中定义版本号的常量,使用ext{}
包裹:
......
// Define versions in a single place
ext {
compileSdkVersion = 30
buildToolsVersion = "30.0.3"
applicationId = "com.legend.demo"
minSdkVersion = 16
targetSdkVersion = 30
versionCode = 1
versionName = "1.0"
}
......
2、在app下的build.gradle中使用 $rootProject.xxx的方式引用即可。
......
android {
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
defaultConfig {
applicationId rootProject.applicationId
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode rootProject.versionCode
versionName rootProject.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
......
}
......
Repositories
Repositories 就是代码仓库,我们平时的添加的一些 dependency 就是从这里下载的,Gradle 支持三种类型的仓库:
Maven、Ivy、以及一些静态文件或者文件夹
gradle 支持多种Maven仓库,除了默认的jCenter()
外,还可以添加国内镜像或公司的私有仓库:
repositories {
maven {
url "http://repo.legend.com/maven"
}
}
如果仓库存在密码,也可以同时传入用户名和密码
repositories {
maven {
url "http://repo.legend.com/maven"
credentials {
username 'legend'
password '123456'
}
}
}
我们还可以使用相对路径配置本地仓库,以此来引用项目中存在的静态文件夹作为本地仓库:
repositories {
flatDir {
dirs "aars"
}
}
如果Gradle下载依赖过慢的情况下我们可以在这里配置阿里云的镜像:
buildscript {
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/'}
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.1"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/'}
}
}
Dependencies
我们在引用库的时候,每个库名称包含三个元素:组名:库名称:版本号,如下:
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
}
如果我们要保证我们依赖的库始终处于最新状态,我们可以通过添加通配符的方式,比如:
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0.+'
implementation 'com.google.android.material:material:1.1.0.+'
}
使用这种做法每次编译都会去网络请求查看新版本,导致编译慢之外,新版本也可能存在很多问题。
依赖方式
dependencies{}
中库的引入有三种方式:compile、implementation、api,它们的区别如下所示:
compile:gradle升级3.+版本后,原来的依赖方法compile替换成了implementation和api,其中api是用来替换compile的。
implementation:当前Module的依赖,使用implementation指令的依赖不会传递。(只编译当前模块,构建速度快)
api:等同于compile,是用来替代compile的方式,使用api指令的依赖会传递。(编译所有模块,构建速度慢)
所谓的依赖传递的意思是:假如ModuleA依赖于ModuleB,而ModuleB依赖于ModuleC,那么ModuleA可以引用ModuleC中的依赖。
本地依赖
未完待续