这个周末的时间可以说都贡献给gradle了,主要是做了什么一个事情呢,也就是查阅各种gradle的资料,到现在还是很多东西没搞清楚,但是也没时间去一一的搞清楚了,再深如一点就要去各个梳理源码了,根据我的这几天的认识,这必将是一个较大的工程,所以暂时打住,这篇文章就来总结下这几天我通过学习对gradle有的一些新的认知。
其实平时构建android项目,只需要网上搜搜官网找找,随便配置下便可,但是这篇文章的目的并不是说明日常配置,而是对gradle配置的一些理解。
1、关于build.gradle文件的groovy语法合理性
我们知道build.gradle是用groovy语言写的配置脚本,那么既然是一门语言其便有固定的语法,为啥build.gradle文件中的内容看来一点都不想我们平时写的代码呢。以project的build.gradle文件为例
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
怎么用goovy写的gradle看起来这么随便,貌似都没有一个正经的方法呢,这个是由于groovy语法比较灵活,当方法只有一个参数时,可以省略掉“()”这个符号,那么allporjects对应的便实际上是
allprojects ({
repositories {
jcenter()
}
})
那么allprojects参数是一个大括号括起来的医堆东西又是啥,这就不得不提到groovy语言中的另一个概念了,那就是Closure这玩意相当于java中的匿名类,里面可以有参数和方法。而这里的repositories实际上就是一个方法,它同样接收一个Closure闭包作为参数。
然后可能有童鞋会说,这个groovy应该是面向对象的语言吧,那么这个allprojects,还有什么的buildscript是怎么总得属于什么对象吧,的确是这样的。答案就是 Project,当 build.gradle 执行时它会配置 Project 实例并将其设为 Delegate 对象,即它的语句块都会被委托给 Project 的实例,我们继续查看 Project Doc,会发现 buildscript 被 ScriptHandler 委托了,接着查看 ScriptHandler Doc 会找到 repositories 和 dependencies 方法。
肯定又有同学要问了,怎么又冒出一个Delegate对象,这又是啥玩意,这里以一个例子来说明吧
class PostHandler {
int count
def info() { "This is Groovy Goodness post #$count!" }
}
def printInfo = {
count++
info()
}
task testClosure{
doLast{
printInfo.resolveStrategy = Closure.DELEGATE_FIRST
// Set delegate to Post object.
printInfo.delegate = new PostHandler(count: 100)
println "This is Groovy Goodness post #101!" == printInfo()
}
}
这里定义的PostHandler实例就是printInfo这个Closure闭包的Delegate(委托),当吧Closure的resolveStrategy 属性设置为Closure.DELEGATE_FIRST的时候,执行闭包函数会执行到Delegate的方法,也就是说Closure 的 Delegate 机制可以使我们将一个闭包中的执行代码的作用对象设置成任意其他对象。
到现在便能理解project的build.gradle文件在在groovy语法上的合理性了吧,于是可能又有同学拿了下面的代码出来
apply plugin: 'com.android.application'
然后说我去,这是啥啊,apply看起来应该是个方法,那么参数就是plugin: ‘com.android.application’,但是这个参数和我们知道的key:value的”key”:”value”这种格式不一样啊,这就得提到 groovy 的 Named arguments 机制了,在goovy中Map可写成name:”yoryky”的格式,key默认就是字符串的,value需钥写明字符类型,所以。。。原来是这样子的。
好了,第一小节,算是说明了gradle的groovy语法合理性了。
2、gradle在android中的应用
第一小节提到的apply plugin: ‘com.android.application’这代码,实际上是用于说明当前的build.gradle文件时用来配置android app的文件,对应项目模块编译后会生成一个apk文件,如果build.gradle文件使用apply plugin: ‘com.android.library’来配置,表示对应项目模块是一个类库,同时编译时会生成一个aar文件。com.android.library 和 com.android.application 内容配置差不太多, 这里以 com.android.application 为例讲解,在讲解com.android.application之前,先来学一些基础知识。
关于gradle这里要搞清楚几个概念,即Project、Task、Plugin、Property、Configuration。
关于Project第一小节有所提及,Project为Task提供了执行上下文,所有的Plugin要么向Project中添加用于配置的Property,要么向Project中添加不同的Task。一个Task表示一个逻辑上较为独立的执行过程,比如编译Java源代码,拷贝文件,打包Jar文件,甚至可以是执行一个系统命令或者调用Ant。另外,一个Task可以读取和设置Project的Property以完成特定的操作。
上面一段话貌似并不能让我们搞清楚,这五个概念。
Project
关于Project这里简单介绍下,它是的gradle对应的默认构建对象,其中有buildscipt、allprojects等方法以及Property定义。
Task
Task对象可以说是我们使用gradle的核心,它就是发动机确保我们的gradle方法能够执行起来,实际上我们看android studio的gradle工具,为我们提供的app的task便能立刻明白Task的作用
点击,这些task便能立即运行task定义的方法。
Property
property其实比较简单,也就是对应我们java中的类的属性,可以设置和获取,但是在groovy中不通过set/get方法来设置获取,而是直接通过参数名称来完成设置获取操作。可以用以下代码来说明
android {
compileSdkVersion 26
buildToolsVersion "27.0.1"
defaultConfig {
applicationId "com.yoryky.mavendemo"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
这里的applicationId实际上会调用DefaultProductFlavor的以下方法
public ProductFlavor setApplicationId(String applicationId) {
this.mApplicationId = applicationId;
return this;
}
来对其mApplicationId这个Property进行赋值,当然这个例子不恰当(后面举个例子弥补,哈哈哈,主要是突然想到,这里的applicationId 、minSdkVersion 等实际上都是方法,我在这个地方一直有疑惑,觉得groovy没有这样设置字段的用法啊,没曾想这里原来是通过方法赋值)。
弥补的例子在这里
class GroovyBean{
private name
}
def bean = new GroovyBean()
task printBean{
doLast{
bean.name = 'this is a bean'
println bean.name
}
}
这里就是直接通过bean.name赋值了。
Plugin
上面的定义说了,所有的Plugin要么向Project中添加用于配置的Property,要么向Project中添加不同的Task,现在再看这句话应该比较清楚了吧。
Configuration
这玩意并不是什么配置,实际上它是一个依赖集合,当配置compile方法的时候
compile 'com.android.support:appcompat-v7:26.+'
实际执行compile的时候会调用到DependencyHandler类的以下方法
/**
* Adds a dependency to the given configuration.
*
* @param configurationName The name of the configuration.
* @param dependencyNotation
*
* The dependency notation, in one of the notations described above.
* @return The dependency.
*/
Dependency add(String configurationName, Object dependencyNotation);
该方法会将configurationName->compile以及dependencyNotation->’com.android.support:appcompat-v7:26.+’封装成一个Dependency对象并添加到Configuration依赖集中,等执行compile的时候,便会根据Configuration依赖集中的内容去添加不同的依赖(这个过程没有看源码,纯粹个人理解,如有问题,请指正)。
到这里五个概念算是理清了,那么再来看看,这段代码
apply plugin: 'com.android.application'
实际上这段代码的作用就是引入’com.android.application’对应的AppPlugin的(这个插件是由gradle提供的),目的就是因为我们要编译并生成apk的一系列的task以及了Android 编译、测试、打包等等的所有task。同时也要注意Project是gradle的默认构建对象,所以app的build.gradle的默认构建对象实际上也是Project实例。
3、gradle使用总结
第一小节讲了build.gradle文件对于groovy语法的合理性,二第二小节讲了gradle在android应用中涉及到的几个概念。到这里可以总结总结了。我认为,gradle工具实际上可以看作Plugin包(个人认为对应项目中的gradle插件版本)、groovy的编译器以及运行管理器(个人认为对应项目gradle编译版本)
它在执行时并不会一开始便顺序执行build.gradle文件中的内容,而是分两个阶段。
第一阶段是配置阶段,第二阶段是实际的执行阶段。
在配置阶段,gradle将读取所有的build.gradle的所有内容来配置project以及task
比如设置project和task的property,处理task之间的依赖关系,初始化表现property还是以下面代码为例说明
android {
compileSdkVersion 26
buildToolsVersion "27.0.1"
defaultConfig {
applicationId "com.yoryky.mavendemo"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
通过applicationId方法,minSdkVersion等方法进行property的初始化,还通过
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
testCompile 'junit:junit:4.12'
}
各种compile将依赖关系添加到Configuration依赖集中,待编译的时候添加依赖集。
在执行阶段,就是运行各种task来完成开发者目的
配置阶段执行顺利的话,实际上运行阶段就是水到渠成的事情了,比我们执行的debug、compile、build、clean等实际上就是在执行阶段。
关于gradle的知识有很多,本文按照自己的思维方式梳理了一下,总结出来就是以下三条
1、gradle.build依照groovy语法配置;
2、关于gradle在android应用中的的五大概念;
3、gradle是编译器以及运行管理器;
参考文献
这两天看了不少的gradle相关的优秀文献,有些文献本文可能没有直接参考,但是优秀的都放这里吧,以便以后参考方便,不用到处找。
5、Gradle学习总结——根本上看透Android Studio构建
9、Gradle插件开发秘籍之断点调试(基于Intellij)
11、Gradle Docs