文章目录

    • what is gradle?
    • 小试牛刀-android中的gradle
    • groovy-学gradle的密钥
    • Project与Task-gradle构建系统
    • 总结

Hi,咱们好啊~
我现已很久都没有更新自己的学习笔记了,感谢咱们这么久以来还把我留在列表里。这么久以来不更新的原因主要是长期在支撑业务需求,导致对根底常识的沉淀大大忽视了。这样是不对的,仍是要源源不断坚持对常识的罗致,才能立于不败之地。

如果看了这篇文章你还不懂gradle,那你还是把我删了吧

这段时间来学习了gradle,也领会到了gradle从开始了解到根本了解,再到深化源码这样一个进程中的一些弯曲。这篇文章主要是gradle的根底常识篇。看完这篇文章,你能够:

  • 清楚gradle的界说和解决的痛点
  • 根本了解Android gradle的运作机制
  • 根本了解gradle的大部分语法
  • 学会根本的groovy开发

假如你想重视gradle更深化的一些常识,请继续重视后续gradle文章。

what is gradle?

先来看一段维基百科上关于gradle的解释。

Gradle是一个依据Apache Ant和Apache
Maven概念的项目主动化构建东西。它运用一种依据Groovy的特定范畴言语来声明项目设置,而不是传统的XML。当时其支撑的言语限于Java、Groovy和Scala,方案未来将支撑更多的言语。

或许刚触摸gradle的同学都不是很了解gradle的这个界说。或许就只会跟着网上的教程copy一点装备,可是不了解这些装备背面的原理。那么怎样来了解这句话呢,咱们能够把握到三个关键:首要,它是一种 构建东西,其次,gradle是 依据maven概念的,最终,运用 groovy这种言语来声明。要了解这几句话,咱们先考虑几个场景。

  1. 途径管理:国内手机商场有大大小小数十个,大的手机厂商也有五六个,每个厂商或许又有不同的定制rom。假如咱们要为不同商场和厂商进行适配,那就需要写这样的代码
if(isHuawei){
// dosomething
} else if(isOppo){
// dosomething
}

这样的话,繁琐不说,对单个手机而言很多的无用代码被编译进apk中,包体积和运转速度都会受影响。为了解决这个问题,gradle引进了productFlavor和buildType的能力,能依据状况来进行打包。所以说他是一个 主动化构建东西。能够看官方文档

  1. 依赖管理:咱们一般会在项目中引入各种三方库进行代码复用。比方,直接手动把jar或许aar copy到项目中,然后添加依赖。这种办法缺点很明显,首要装备和删去流程很繁琐,其次,同一个jar或许会被多个项目所引证,导致不知不觉就copy了多个jar。最终,版别管理困难。为了解决这个问题,gradle是依据maven库房,装备和删去的时分仅需要对库房的坐标进行操作,一切的库都会被gradle统一管理,大多数状况下每个库只会有一个版别存在于项目中,而且每个库只会有一个副本存在于项目中。

所以gradle其实不是什么神秘的东西,仅仅依据某种言语(groovy, java, kotlin)的一种构建东西而已。只要咱们大概把握了根本的用法和他的内部原理,日常工作中就会知道自己网上搜到的指令是什么意思啦。skr~

小试牛刀-android中的gradle

如果看了这篇文章你还不懂gradle,那你还是把我删了吧

咱们先看看日常工作中常常用到的几个gradle文件。能够看到主要有有三个文件:

  1. build.gradle 根文件下放的一般放的是针对整个工程的通用装备,每个module下面的build.gradle文件是针对每个module自身的装备。
buildscript {
	ext.kotlin_version = '1.2.71'
	repositories {
		google()
		jcenter()
	}
	dependencies {
		classpath 'com.android.tools.build:gradle:3.2.1'
		classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
	}
}
allprojects {
	repositories {
		google()
		jcenter()
	}
}

这是一个默许的装备,咱们能够看到有buildscript,allprojects,repositories,dependencies几个装备项,这些装备项是干嘛的呢,许多的同学在刚学gradle的时分都是一脸懵逼的。这些其实是gradle的一种特定的语法,咱们称之为DSL(domain-specific language)。能够参考官网。这儿能够看到allprojects代理的是每个project,能够了解成咱们的每个module,也便是对咱们所写的每个module的装备。buildscript主要装备的是打包相关的东西,比方gradle版别,gradle插件版别等,这些都是针对构建东西自己的装备。repositories,dependencies是三方库的库房和坐标。所以根目录的build.gradle适当于是全体的装备。

而module下的build.gradle主要是android,dependencies等装备项。

apply plugin:'com.android.application'
android{
	...
}
dependencies{
	...
}

或许有些同学会感到奇怪,为啥咱们在官网没有看到android这个装备项呢?这个主要是由于它并不是gradle的DSL,某种含义上说应该算是android特有的,是通过Android的插件’com.android.application’带进来的装备项。咱们假如把第一行删掉,就会发现android{}这个装备项找不到了。

所以,咱们能够发现,build.gradle里边的装备项,要么是gradle自带的,要么是各种插件界说的。有不认识的装备项,就去官网查询一下就好了,授人以鱼不如授人以渔嘛。咱们后边也会解说到引进插件的办法和怎样界说插件和装备项。

  1. settings.gradle 这个文件主要是决定每个module是否参与构建。咱们能够这样去了解,settings.gradle适当于是每个module的开关,关上了这个module就不能运用了,其他依赖到它的module也都会出问题。
  2. gradle.properties 这儿主要是添加和修改一些能够在构建进程中直接运用的参数。不仅仅能够添加自界说参数,还能够修改系统的参数哦~

总结一下,便是说根目录下有一个build.gradle,处理整个工程的装备项,根目录下的settings.gradle装备整个工程中参与构建的module,每个module自己有一个build.gradle,处理自己模块的装备。这便是android构建的一个大概状况。当然,看了这一部分必定仍是不明白怎样去写的,接下来咱们走进代码层面。

groovy-学gradle的密钥

gradle能够运用groovy,kotlin,java等言语进行书写,可是groovy相对来说是现在比较盛行的gradle装备办法,下面咱们解说一点groovy根底。不讲太多,够用就行。

  1. 字符串

groovy的字符串分为两种java.lang.String和groovy.lang.GString。其间单引号和三引号是String类型的。双引号是GString类型的。支撑占位插值操作。和kotlin相同,groovy的插值操作也是用 ${}或许 $来标示, ${}用于一般替代字串或许表达式, $主要用于A.B的方式中。

def number = 1
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }"
println eagerGString
println lazyGString
number = 2
println eagerGString
println lazyGString
  1. 字符Character

Groovy没有清晰的Character。可是能够强行声明。

char c1 ='A'
assert c1 instanceof Character
def c2 = 'B' as char
assert c2 instanceof Character
def c3 =(char)'C'
assert c3 instanceof Character
  1. List

Groovy的列表和python的很像。支撑动态扩展,支撑放置多种数据。运用办法支撑def和直接界说。还能够像python那样索引

//List中存储任意类型
def heterogeneous =[1,"a",true]
//判别List默许类型
def arrayList =[1,2,3]
assert arrayList instanceof java.util.ArrayList
//运用as强转类型
def linkedList =[2,3,4] as LinkedList
assert linkedList instanceof java.util.LinkedList
//界说指定类型List
LinkedList otherLinked =[3,4,5]
assert otherLinked instanceof java.util.LinkedList
// 像python相同索引
assert letters[1] == 'b'
//负数下标则从右向左index
assert letters[-1] == 'd'
assert letters[-2] == 'c'
//指定item赋值判别
letters[2] = 'C'
assert letters[2] == 'C'
//给List追加item
letters <<'e'
assert letters[4] == 'e'
assert letters[-1] == 'e'
//获取一段List子集
assert letters[1,3] ==['b','d']
assert letters[2..4] == ['C','d','e']
  1. Map
//界说一个Map
def colors =[red:'#FF0000',green:'#00FF00', blue:'#0000FF']
//获取一些指定key的value进行判别操作
assert colors['red']== '#FF0000'
assert colors.green == '#00FF00'
  1. 运算符

**:次方运算符。

?.:安全占位符。和kotlin相同防止空指针反常。

.@:直接域拜访操作符。由于Groovy主动支撑特点getter办法,但有时分咱们有一个自己写的特别getter办法,当不想调用这个特别的getter办法则能够用直接域拜访操作符。这点跟kotlin的

.&:办法指针操作符,由于闭包能够被作为一个办法的参数,假如想让一个办法作为另一个办法的参数则能够将一个办法当成一个闭包作为另一个办法的参数。

?::二目运算符。与kotlin中的相似。

*.打开运算符,一个调集运用打开运算符能够得到一个元素为原调集各个元素执行后边指定办法所得值的调集。

cars = [new Car(make:'Peugeot',model:'508'),null,newCar(make:'Renault',model:'Clio')]
assert cars*.make == ['Peugeot',null,'Renault']
assert null*.make == null 
  1. 闭包

groovy里比较重要的是闭包的概念。官方界说是“Groovy中的闭包是一个敞开,匿名的代码块,能够接受参数,返回值并分配给变量”。其实闭包跟kotlin的lambda函数很像,都是先界说后执行。可是又有一些细微的区别。接下来咱们细讲讲gradle的闭包。

闭包是能够用作办法参数的代码块,Groovy的闭包更象是一个代码块或许办法指针,代码在某处被界说然后在其后的调用处执行。一个闭包实际上便是一个Closure类型的实例。写法和kotlin的lambda函数很像。

咱们常见的闭包是这样的

//最根本的闭包 
{ item ++ } 
//运用->将参数与代码别离 
{ item -> item ++ } 
//运用隐含参数it 
{ println it } 
//运用显现的名为参数 
{ name -> println name } 
// 调用办法 
a.call () 
a () 
// Groovy的闭包支撑最终一个参数为不定长可变长度的参数。 
def multiConcat = { int n , String ... args -> 
	args . join ( '' )* n 
	}

咱们要注意,假如咱们单纯的仅仅写成 a = { item++ }, 这仅仅界说了一个闭包,是不能运转的。必须调用a.call()才能运转出来。所以咱们能够了解了,闭包便是一段代码块而已。当咱们有需要的时分,能够去运转它,这么一想是不是和lambda函数很像?

假如你看了官网,你会发现有一些这样的说法,

如果看了这篇文章你还不懂gradle,那你还是把我删了吧

什么叫做delegate?这儿涉及到闭包内部的三种目标。

  • this 对应于界说闭包的那个类,假如在内部类中界说,指向的是内部类
  • owenr 对应于界说闭包的那个类或许闭包,假如在闭包中界说,对应闭包,不然同this共同
  • delegate 默许是和owner共同,或许自界说delegate指向

this和owner都比较好了解。咱们能够用闭包的getxxx办法获取

def thisObject = closure.getThisObject()
def ownerObject = closure.getOwner()
def delegate = closure.getDelegate()

重头戏仍是delegate这个目标。闭包能够设置delegate目标,**设置delegate的含义便是将闭包和一个详细的目标关联起来。**咱们先来看个比方,这儿以自界说android闭包为例。

android {
	compileSdkVersion 25
	buildToolsVersion "25.0.2"
	defaultConfig {
		minSdkVersion 15
		targetSdkVersion 25
		versionCode 1
		versionName "1.0"
	}
}

这个闭包对应的实体类是两个。

# Android.groovy
class Android { 
	private int mCompileSdkVersion
 	private String mBuildToolsVersion 
 	private ProductFlavor mProductFlavor 
 	Android () { 
 		this.mProductFlavor = new ProductFlavor () 
 	} 
 	void compileSdkVersion ( int compileSdkVersion ) { 
 		this.mCompileSdkVersion = compileSdkVersion 
 	} 
 	void buildToolsVersion ( String buildToolsVersion ) { 
 		this.mBuildToolsVersion = buildToolsVersion 
	 } 
 	void defaultConfig ( Closure closure ) { 
 		closure.setDelegate ( mProductFlavor ) 
 		closure.setResolveStrategy ( Closure.DELEGATE_FIRST ) 
 		closure.call () 
	 } 
 	@Override String toString () { 
 		return "Android{" + "mCompileSdkVersion=" + mCompileSdkVersion + ", mBuildToolsVersion='" + mBuildToolsVersion + '\'' + ", mProductFlavor=" + mProductFlavor + '}' 
 	} 
 } 
# ProductFlavor.groovy 
class ProductFlavor { 
	private int mVersionCode 
	private String mVersionName 
	private int mMinSdkVersion 
	private int mTargetSdkVersion 
	def versionCode ( int versionCode ) { 
		mVersionCode = versionCode 
	} 
	def versionName ( String versionName ) { 
		mVersionName = versionName 
	} 
	def minSdkVersion ( int minSdkVersion ) { 
		mMinSdkVersion = minSdkVersion 
	} 
	def targetSdkVersion ( int targetSdkVersion ) { 
		mTargetSdkVersion = targetSdkVersion 
	} 
	@Override String toString () {
		return "ProductFlavor{" + "mVersionCode=" + mVersionCode + ", mVersionName='" + mVersionName + '\'' + ", mMinSdkVersion=" + mMinSdkVersion + ", mTargetSdkVersion=" + mTargetSdkVersion + '}' 
	}
}

然后界说的时分就写成

//闭包界说 
def android = { 
	compileSdkVersion 25 
	buildToolsVersion "25.0.2" 
	defaultConfig { 
		minSdkVersion 15 
		targetSdkVersion 25 
		versionCode 1 
		versionName "1.0"
	} 
} 
//调用 
Android bean = new Android () 
android.delegate = bean 
android.call() 
println bean.toString() 
//打印成果 
Android { 
	mCompileSdkVersion = 25, 
	mBuildToolsVersion = '25.0.2', 
	mProductFlavor = ProductFlavor { 
		mVersionCode = 1 , 
		mVersionName = '1.0' , 
		mMinSdkVersion = 15 , 
		mTargetSdkVersion = 25 
	}
}

这样就能将闭包中声明的值,赋给两个目标Android和ProductFlavor来处理了。

上面官网的图里,说ScriptHandler被设置成buildscript的delegate。意思便是buildscript界说的参数被ScriptHandler拿来运用了。咱们有兴趣的能够去看看ScriptHandler的源码~

Project与Task-gradle构建系统

上面咱们讲完了根本的用法,咱们或许懂gradle的装备和写法了。可是或许仍是不明白gradle的构建系统到底是怎样样的。这儿咱们就要深化进gradle的构建系统Project和Task了。下面的东西看着就要动动脑筋了。

1.Task Task是gradle脚本中的最小可执行单元。类图如下:

如果看了这篇文章你还不懂gradle,那你还是把我删了吧

值得注意的是由于Gradle构建脚本默许的姓名是build.gradle,当在shell中执行gradle指令时,Gradle会去当时目录下寻觅姓名是build.gradle的文件。所以只要界说在build.gradle中的Task才是有效的。

能够通过三种办法来声明task。咱们能够依据自己的项目需要去界说Task。比方自界说task接管gradle的编译进程

task myTask2 << {
	println "doLast in task2" 
} 
//选用 Project.task(String name) 办法来创立 
project.task ( "myTask3" ).doLast { 
	println "doLast in task3" 
} 
//选用 TaskContainer.create(String name) 办法来创立 
project.tasks.create ( "myTask4" ).doLast { 
	println "doLast in task4" 
}

TaskContianer 是用来管理一切的 Task 实例调集的,能够通过 Project.getTasks() 来获取 TaskContainer 实例。常见接口:

findByPath(path:String):Task
getByPath(path:String):Task
getByName(name:String):Task
withType(type:Class):Task
Collectionmatching(condition:Closure):TaskCollection
//创立taskcreate(name:String):Task
create(name:String):Task
create(name:String,configure:Closure):Task
create(name:String,type:Class):Task
create(options:Map<String,?>):Task
create(options:Map<String,?>,configure:Closure):Task
//当task被加入到TaskContainer时的监听
whenTaskAdded(action:Closure)

Gradle支撑增量编译。了解过编译profile文件的朋友都知道,里边有很多的task都是 up-to-date。那么这种up-to-date是什么意思呢。 Gradle的Task会把每次运转的成果缓存下来,当下次运转时,会查看一个task的输入输出有没有改变。假如没有改变便是up-to-date,越过编译。

如果看了这篇文章你还不懂gradle,那你还是把我删了吧

2. Project 先从Project目标讲起,Project是与Gradle交互的主接口。android开发中最为咱们所了解的便是build.gradle文件,这个文件与Project是1对1的关系,build.gradle文件是project目标的委托,脚本中的装备都是对应着Project的Api。Gradle构建进程启动的时分会依据build.gradle去实例化Project类。也便是说,构建的时分,每个build.gradle文件会生成一个Project目标,这个目标负责当时module的构建。

Project本质上是包含多个Task的容器,一切的Task存在TaskContainer中。

如果看了这篇文章你还不懂gradle,那你还是把我删了吧

咱们从姓名能够看出dependencies, configuration, allprojects, subprojects, beforeEvaluate, afterEvaluate这些都是咱们常见的装备项,在build.gradle文件中接收一个闭包Closure。

好了,现在咱们现已聊了build.gradle了,可是咱们都知道,咱们项目中还有一个settings.gradle呢,这个是拿来干嘛的呢?这就要说到Project的 Lifecycle了,也便是Gradle构建Project的步骤,看官网原文:

  • Create a Settings instance for the build.
  • Evaluate the settings.gradle script, if present, against the Settings
    object to configure it.
  • Use the configured Settings object to create the hierarchy of Project
    instances.
  • Finally, evaluate each Project by executing its build.gradle file, if
    present, against the project. The projects are evaluated in
    breadth-wise order(宽度搜索), such that a project is evaluated before its
    child projects. This order can be overridden by calling
    Project.evaluationDependsOnChildren() or by adding an explicit
    evaluation dependency using
    Project.evaluationDependsOn(java.lang.String).

也便是说,Project目标依赖Settings目标的构建。咱们常在settings.gradle文件中装备需要引入的module,便是这个原因。

3.Property 看完了build.gradle和settings.gradle,接下来咱们讲讲gradle.properties。这个文件存放的键值对方式的特点,这些特点能被项目中的gradle脚本运用ext.xxx所拜访。

咱们也能够运用Properties类来动态创立特点文件。如:

def defaultProps = newProperties()
def aultProps.setProperty("debuggable",'true')
def aultProps.setProperty("groupId",GROUP)

而且特点能够承继,在一个项目中界说的特点能够主动被子项目承继。所以在哪个子项目都能够运用project.ext.xxx拜访。不同子项目间选用通用的装备插件来装备

apply from:rootProject.file('library.gradle')

总结

通过上面的学习,咱们应该现已了解了gradle的根本装备,写法和比较浅显的内部原理了。由于篇幅原因,深化的内容咱们放在下一篇。敬请期待《一篇文章深化gradle》