参考资料:
https://mp.weixin.qq.com/s/1UHcYOudViMhpUYeREZzGA
https://mp.weixin.qq.com/s/hCXLz-9BnAOGWyZ2eRikKg
Gradle是一个项目自动化建构工具,它使用一种基于Groovy的特定领域语言来声明项目设置,而不是传统的XML。Gradle主要帮我们做了依赖,打包,部署,发布,各种渠道的差异管理等工作。当前其支持的语言限于Java、Groovy和Scala,计划未来将支持更多的语言。
Android Gradle 的 Project 和 Tasks
这个Gradle中最重要的两个概念。每次构建(build)至少由一个project构成,一个project 由一到多个task构成。项目结构中的每个build.gradle文件代表一个project,在这编译脚本文件中可以定义一系列的task;task 本质上又是由一组被顺序执行的Action`对象构成,Action其实是一段代码块,类似于Java中的方法
Android Gradle 构建生命周期
每次构建的执行本质上执行一系列的Task。某些Task可能依赖其他Task。哪些没有依赖的Task总会被最先执行,而且每个Task只会被执行一遍。每次构建的依赖关系是在构建的配置阶段确定的。每次构建分为3个阶段:
这是创建Project阶段,构建工具根据每个build.gradle文件创建出一个Project实例。初始化阶段会执行项目根目录下的settings.gradle文件,来分析哪些项目参与构建。
所以这个文件里面的内容经常是:
这是告诉Gradle这些项目需要编译,所以我们引入一些开源的项目的时候,需要在这里填上对应的项目名称,来告诉Gradle这些项目需要参与构建。
这个阶段,通过执行构建脚本来为每个project创建并配置Task。配置阶段会去加载所有参与构建的项目的build.gradle文件,会将每个build.gradle文件实例化为一个Gradle的project对象。然后分析project之间的依赖关系,下载依赖文件,分析project下的task之间的依赖关系。
这是Task真正被执行的阶段,Gradle会根据依赖关系决定哪些Task需要被执行,以及执行的先后顺序。
task是Gradle中的最小执行单元,我们所有的构建,编译,打包,debug,test等都是执行了某一个task,一个project可以有多个task,task之间可以互相依赖。例如我有两个task,taskA和taskB,指定taskA依赖taskB,然后执行taskA,这时会先去执行taskB,taskB执行完毕后在执行taskA.
那我们生成的task都在哪里呢?
答:点击你android studio 右上角的gradle按钮,如下:
将项目模式切换为Android,然后查看Gradle Scripts目录就可以看到每个Module对应的bulid.gradle文件了,我们就先从gradle-wrapper.properties文件开始说起。
首先Wrapper是对Gradle的一层包装,便于开发过程中统一Gradle构建的版本号,Gradle提供内置的Wrapper task帮助我们自动生成Wrapper所需的目录文件,那么Wrapper task帮我们自动生成了那些文件呢?如图:
还有我们项目里gradle\wrapper里的内容:
下面来说一下每一个字段及其说明
字段 | 字段的说明 |
---|---|
distributionBase | 下载Gradle压缩包解压后存储的主目录 |
distributionPath | 相对于distributionBase解压后的压缩包的位置,默认都在c盘/用户/gradle路径下 |
zipStoreBase | 和distributionBase类似,区别是存放zip压缩包 |
zipStorePath | 和distributionPath类似,区别是存放zip压缩包 |
distributionUrl | gradle压缩包的下载地址 |
distributionUrl下载路径下载的压缩包在本地的地址默认为:
此文件夹包含了各个版本你下载的Gradle。
ok,我们先看看settings.gradle文件中都有哪些内容:
此文件一看便知:如果我们的Project依赖了N个本地的module那么此处就会有N个引入。
话不多说上图:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
jcenter()
//友盟新maven仓库地址
maven { url 'https://dl.bintray.com/umsdk/release' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
//Butterknife插件依赖
classpath 'com.jakewharton:butterknife-gradle-plugin:10.0.0'
}
}
//Android 统一版本号管理
ext {
//编译版本号
globalCompileSdkVersion = 28
//编译工具版本
globalBuildToolVersion = "28.0.2"
//当前sdk版本号
globalTargetSdkVersion = 28
//最低sdk版本号
globalMinSdkVersion = 19
androidDependencies = [
design : "com.android.support:design:${supportLibraryVersion}",
appcompat: "com.android.support:appcompat-v7:${supportLibraryVersion}",
]
}
allprojects {
repositories {
google()
mavenCentral()
jcenter()
//友盟新maven仓库地址
maven { url 'https://dl.bintray.com/umsdk/release' }
//BRVAH 框架引入
maven { url "https://jitpack.io" }
//Banner
maven { url "https://www.jitpack.io" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
上面Project的build gradle文件中的代码都是Groovy语言,Groovy是基于JVM虚拟机的一种动态语言,它的语法和Java非常相似,它就是一门非常灵活的动态脚本语言。
Project的build gradle文件中的代码主要分五个部分来讲解:
buildscript中的声明是gradle脚本自身需要使用的资源。可以声明的资源包括依赖项、第三方插件、maven仓库地址等.
ext是自定义属性,本人现在喜欢将所有module公用的编译版本号,当前编译工具版本等公用的信息进行在project的build.gradle中进行自定义给所有module使用同一版本,方便管理。当然你也可以自定义一个Version.gradle文件,这样也可以在其中添加常用三方依赖库的版本信息,方便module统一调用,如果都在project的build.gradle中进行自定义东西太多,不方便,具体实现我就不说了。
repositories顾名思义就是仓库,而jcenter()、maven()和google()就是托管第三方插件的平台,平时开发中我们可能会遇到一些插件需要,也是在此配置。
我们配置了repositories仓库后还需要在dependencies{}里面进行配置,把需要配置的依赖用classpath配置上,因为这个dependencies在buildscript{}里面,所以代表的是Gradle需要的插件。
task clean(type: Delete) {
delete rootProject.buildDir
}
好多小小伙伴平时可以没太注意这个脚本的作用:
此脚本的作用是在运行gradle clean时,执行此处定义的task。该任务继承自Delete,意思是删除根目录中多余的build目录或文件。和Android Studio的clean差不多。
其实还有个疑问:buildscript中的repositories和allprojects中的repositories有什么区别呢?
答:
buildscript中的声明是gradle脚本自身需要使用的资源,而allprojects声明的却是你所有module所依赖的资源,比如你的每个module都需要用同一个第三库的时候,你可以在allprojects里面声明。
首先我们先看:
这种叫做引入Gradle插件,而Gradle插件大致分为分为两种:
apply plugin:'×××':这种属于二进制插件,一般是被打包在jar里独自发布,如果你自定义过插件,你就会知道,通常在发布的时候要指定plugin id,默认为完全限定名。
apply from:'×××':叫做应用脚本插件,严格意义来说它属于一个脚本插件,和二进制插件不同的是它使用的是from关键字.后面紧跟的坫一个脚本文件,可以是本地的,也可以是网络存在的,如果是网络上的话要使用HTTP URL
Gradle插件的分类:
Gradle插件的主要分为三类:
一般一个项目只会设置一个App插件,而module一般是会设置为Library插件,如果你学习过组件化你就会有一个清晰的认知。
android {
compileSdkVersion rootProject.ext.globalCompileSdkVersion
defaultConfig {
applicationId "com.hxzk_bj_demo"
minSdkVersion rootProject.ext.globalMinSdkVersion
targetSdkVersion rootProject.ext.globalTargetSdkVersion
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//打包只把英文和中文打包进去,其他忽略
resConfigs 'zh','zh-rCN'
// 设置支持的 SO 库构架
ndk {abiFilters "armeabi", "armeabi-v7a" }
//此段代码解决的是因为引入了第三方jar包,而项目里方法数过多,需要分包,android项
目有方法数限制,最多64K。
multiDexEnabled true
//添加如下配置解决Butterknife 引起的Annotation processors must be explicitly declared now.
javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
}
}
//声明下so文件的存放路径就行了,修改jniLibs的默认路径为libs
sourceSets {
main {
//配置so加载目录
jniLibs.srcDirs = ['libs']
}
}
我们一个来说明其含义:
是Android插件提供的一个扩展类型,可以让我们自定义Android Gradle工程,是Android Gradle工程配置的唯一入口
是编译所依赖的Android SDK的版本
是构建该Android工程所用构建工具的版本
defaultConfig里面都是工程的默认配置
配置我们的包名,包名是app的唯一标识。默认跟AndroidManifest里面的package包名一致,但其实他跟AndroidManifest里面的package是可以不同的,他们之间并没有直接的关系。
package指的是代码目录路径;applicationId指的是app对外发布的唯一标识,注意不要混淆。
是支持的Android系统的api level
表明我们是基于哪个Android版本开发的,也就是开发版本
app应用内部版本号,一般用于控制app升级
app应用的版本名称
通过resConfig指定我们需要的strings资源,包括过滤 drawable文件夹的资源等
用于配置该BuildType是否启用自动拆分多个Dex的功能,通常用在方法数超过65535的解决办法
多平台编译,生成有so包的时候使用,包括四个平台'armeabi', 'x86', 'armeabi-v7a', 'mips'
Java源代码文件和资源文件的集合,我们可以通过sourceSets更改源集的Java目录或者资源目录等。
签名配置,是标记该app的唯一性。storeFile签名文件,storePassword签名证书文件的密码,storeType签名证书类型,keyAlias签名证书中秘钥别名,keyPassword签名证书中改密钥的密码。
默认情况下,debug模式的签名已经被配置好了,使用的就是Android SDK自动生成的debug证书,它一般位于$HOME/.android/debug.keystore,其key和密码是已经知道的,一般情况下我们不需要单独配置debug模式的签名信息
构建类型,在Android Gradle工程中,它已经帮我们内置了debug和release两个构建类型,两种模式主要车别在于,能否在设备上调试以及签名不一样,其他代码和文件资源都是一样的
Gradle的多渠道打包,如下我们定义了yingyongbao 平台和yingyongbaovip 平台生成apk的名称。
productFlavors {
yingyongbao {
manifestPlaceholders = [APP_NAME: "APP普通版"]
}
yingyongbaovip {
manifestPlaceholders = [APP_NAME: "App付费版"]}
}
占位符,我们可以通过它动态配置AndroidManifest文件一些内容,譬如app的名字:
上面就是我们引用了在productFlavors 中定义的不同平台对应的应用名称。
yingyongbao {
manifestPlaceholders = [APP_NAME: "随意普通版"]
buildConfigField("boolean", "isVipVersion", "false")
}
yingyongbaovip {
manifestPlaceholders = [APP_NAME: "随意付费版"]
buildConfigField("boolean", "isVipVersion", "true")
}
buildConfigField是BuildConfig文件的一个变量,而BuildConfig这个类是Android Gradle构建脚本在编译后生成的,生成的位置在:
那这个buildConfigField有什么作用呢?通常app针对不用的用户群体可能会出一个普通版和收费版,根据这个值去区分:
先上图:
flavorDimensions英文意思是维度,作用是可以在不同的包中形成不同的applicationId和versionName,也就是说可以生成两个app,在同一手机上测试。因为applicationId的值不同嘛,
关于这个dependencies的含义及3.0之后如gradle 3.0中依赖implementation、api的区别,我上一篇文章有详解过,这里就不说了参考Android -Gradle依赖导入及相关知识