本文以前文写的Posdevice和Version2Asset这2个工程为例说说gradle基础概念,主要介绍delegate、sb、常见task以及如何插入task。
gradle是配置型脚本,每个gradle文件的存在意义就是配置某个对象,一个gradle文件执行的时候,会配置一个对象。比如build.gradle会配置一个PRoject对象,我们称这个build.gradle的委托对象。脚本(gradle文件)和委托对象的对照表如下:
Type of script | Delegates to instance of | 对应文件 |
---|---|---|
Build script | Project | build.gradle |
Init script | Gradle | – |
Settings script | Settings | setting.gradle |
在脚本里可以用委托对象的方法或者属性,这句话怎么理解?举个例子
//root build.gradle// Top-level build file where you can add configuration options common to all sub-projects/modules.println "root build.gradle execute"buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files }}subprojects{//为每个子 Project 加载 utils.gradle 。当然,这句话必须位于subprojects之内 apply from: rootProject.getRootDir().getAbsolutePath() + "/utils.gradle"}allprojects { repositories { jcenter() }}task clean(type: Delete) { delete rootProject.buildDir}在上边的代码里,实际上我们可以看到脚本里的东西有3种类型,第一种是L3 println开头的语句(groovy代码结尾不需要加分号);第二种是buildscript,subprojects,allprojects这3个大括号形式的东西(后面我们会知道他们叫script block);第三种是定义了一个clean的task。 实际上buildscript、subprojects、allprojects都是Project类(org.gradle.api. Project)的方法,这就是在脚本里可以用委托对象的方法或者属性,在这里脚本就是build.gradle文件,而脚本的委托对象是一个Project对象,所以我们可以在脚本里使用Project的类或者方法。 我也挺震惊的,buildscript、subprojects、allprojects居然是方法,我之前一直以为是脚本就这么配置的,记住就行。 实际上buildscript的完全体应该是如下,buildscript接收闭包参数,而闭包就是这个{},groovy的语法规定,如果只有一个参数是闭包,那么可以省略(),所以就变成了上文的样子。实际上这段代码的意思是执行buildscript函数,跟我们执行println是类似的,都是方法执行。
buildscript ({ repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files }})gradle给这种类型的方法起了个新名字叫做script block,script block是gradle里的一个非常重要的概念.
A script block is a method call which takes a closure as a parameter
实际上下面这段代码也是函数调用,调用了一个task的函数,最后一个参数为闭包。
task clean(type: Delete) { delete rootProject.buildDir}我们看到build.gradle内有script block(简称sb),buildscript{。。。}和subprojects{。。。},是不是都是project来执行这2个sb呢?不对。我们知道闭包有delegate的概念,给闭包设置delegate,就能让闭包的执行主体改变。这里正是如此,buildscript内闭包的delegate是一个ScriptHandler。可以看buildscript的注释,如下所示,注意against this project's {@link ScriptHandler}
,这就是指定delegate的意思。同理subprojects函数的闭包参数的target是当前project的子project,其实就是2个subproject.
Block | Description |
---|---|
allprojects { } | Configures this project and each of its sub-projects. |
artifacts { } | Configures the published artifacts for this project. |
buildscript { } | Configures the build script classpath for this project. |
configurations { } | Configures the dependency configurations for this project. |
dependencies { } | Configures the dependencies for this project. |
repositories { } | Configures the repositories for this project. |
sourceSets { } | Configures the source sets of this project. |
subprojects { } | Configures the sub-projects of this project. |
publishing { } | Configures the PublishingExtension added by the publishing plugin. |
注意subprojects和allprojects的区别,allprojects=subprojects+root project
这个时候有个问题,我们发现build.gradle支持的sb下并没有android{},那为什么下边的build.gradle里有android{},原因是我们引入了com.android.application这个插件。那android内闭包的delegate是什么呢?看官方文档可知,app插件会让闭包的delegate为AppExtension。
apply plugin: 'com.android.application'println 'app build.gradle execute'android {// 采用api导入的方式 compileSdkVersion gradle.api// 利用gradle.properties buildToolsVersion buildToolsVer defaultConfig { applicationId "com.fish.posdevice" minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }}dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') 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:25.0.1' testCompile 'junit:junit:4.12' compile project(':cposdevicesdk')}所以在andorid内,我们可以用哪些sb,就看AppExtension支持什么。看到了熟悉的aapt,dex, signingConfigs
Block | Description |
---|---|
aaptOptions { } | Configures aapt options. |
adbOptions { } | Configures adb options. |
buildTypes { } | Configures build types. |
compileOptions { } | Configures compile options. |
dataBinding { } | Configures data binding options. |
defaultConfig { } | The default configuration, inherited by all product flavors (if any are defined). |
dexOptions { } | Configures dex options. |
externalNativeBuild { } | Configures external native build options. |
jacoco { } | Configures JaCoCo options. |
lintOptions { } | Configures lint options. |
packagingOptions { } | Configures packaging options. |
productFlavors { } | Configures product flavors. |
signingConfigs { } | Configures signing configs. |
sourceSets { } | Configures source sets. |
splits { } | Configures APK splits. |
testOptions { } | Configures test options. |
比如下边这段代码,我们怎么知道buildTypes里面可以配置minifyEnabled,proguardFiles,signingConfig。首先我们要看buildTypes这个方法的delegate,查官方文档可知buildTypes的delegate是BuildType,而看BuildType可知他支持minifyEnabled,proguardFiles,signingConfig等属性,所以我们才能在这里这么配置
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } demo { //和debug使用同一个签名 signingConfig signingConfigs.debug } }对应module LifeCycle。这是一个有一个app,一个lib的android studio工程的gradle构建的基本流程。
x-Pro:LifeCycle fish$ ./gradlew assemble -qsetting.gradle execute begin...setting.gradle execute finish...root build.gradle execute begin...root build.gradle execute end...app build.gradle execute begin...app build.gradle execute end...app afterProject appapp beforeProject mylibrarylib build.gradle execute begin...lib build.gradle execute end...app afterProject mylibrarytaskGraph.whenReady。。。执行taskapp buildFinished我一般常用asemble/asembleDebug/clean.
build,assemble,clean build可切换, build variant
初学gradle时,很想知道各个任务之间的依赖关系,但是发现没什么人提到这个,后来我明白,原来是android gradle插件太复杂了,随随便便一两百个任务。看task依赖关系,可以用visteg插件,比较好,能够图形化展示依赖关系
使用visteg插件很简单,只要在root的build.gradle下加以下代码就好了
buildscript { repositories { jcenter() } dependencies { classpath 'cz.malohlava:visteg:1.0.0' }}apply plugin: 'cz.malohlava.visteg'visteg { enabled = true colouredNodes = true colouredEdges = true destination = 'build/reports/visteg.dot' exporter = 'dot' colorscheme = 'spectral11' nodeShape = 'box' startNodeShape = 'hexagon' endNodeShape = 'doubleoctagon'}然后我们执行./gradlew build
就会生成dot文件,位于build/reports/,这个文件里记载了依赖关系。
怎么看这个dot文件呢?需要graphviz,可以用homebrew装上graphviz brew install graphviz
然后执行下边的命令就可以了,把dot转化为png,看png就好了。
此时我们得到了一个png图,一个dot文件,2者对照着看,task依赖关系就很清晰了。
visteg的github地址https://github.com/mmalohlava/gradle-visteg
以Version2Asset为例,这是个单module工程。
assemble依赖于assembleDebug, assembleRelease, assembleDemoassembleDebug依赖于compileDebugSources,packageDebugas的build—>make project是执行compileDebugSources、compileDebugAndroidTestSources(注意assmbleDebug不会执行compileDebugAndroidTestSources)as的build—>rebuild project是执行clean、compileDebugSources、compileDebugAndroidTestSourcesas内build—>make project后可以在message里看到如下信息,这就是make project执行的task,看着很多,其实核心就是compileDebugSources和compileDebugAndroidTestSources。
Information:Gradle tasks [:app:generateDebugSources, :app:generateDebugAndroidTestSources, :app:mockableAndroidJar, :app:prepareDebugUnitTestDependencies, :app:compileDebugSources, :app:compileDebugAndroidTestSources, :app:compileDebugUnitTestSources]而rebuild project会多执行一个clean
我们写自己的构建代码的时候常常要在原有task的基础上插入一些自己的task,如何插入呢?
task之间的依赖关系都是通过dependson这个函数形成的。 B.dependsOn A 是把A加到B的依赖集(一个set)里去,并不是说B只依赖于A,他很可能还依赖于很多task。同时A也可能被很多人依赖,所有的task之间是一个有向图的关系。一个task可以被多次依赖,但不会多次执行。 我们为了自己的构建需求经常需要插入自己的task,如何插呢? 比如,已知clean->b,我想插一个iamc,即clean->c->b,怎么做? 代码如下
task b<<{ println 'task bb'}task c<<{ println 'task cc'}clean.dependsOn 'b'project.afterEvaluate {// insert task between clean and 'b' clean.dependsOn(c) c.dependsOn(b)}其实,此时clean->c,b 但是c->b,所以b肯定先执行,而且只执行一次,所以我们可以写clean->c->b。
task的执行顺序可以看这篇文章,译文是这里
比如task clean->some tasks,我们不想知道some tasks是哪些task,我们只是想在clean和some tasks之间插入一个c,怎么做?可以先查到ome tasks,然后让clean-》c,c->some tasks,代码如下(local project:TaskInsert)
void copyDependsOn(Task base, Task task) { for (Object depend : base.dependsOn) { task.dependsOn(depend) }}task b<<{ println 'task bb'}task c<<{ println 'task cc'}clean.dependsOn 'b'project.afterEvaluate {// insert task before clean copyDependsOn(clean, c) clean.dependsOn(c)}在某个task之前插代码还可以用doFirst,来操作,如下,在clean之前加代码。doFirst实际上是往一个task的actionList内加一个闭包,并且放在头部。
clean.doFirst{ println 'clean first'}比如下边就是找到javaCompile类型的task,并且给这个task设置依赖关系。这样在compileTask执行前,必定会执行abc
task abc<<{ println 'abc'}tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn abc}通过这篇文章知道了,哪些sb可以写在build.gradle里,哪些sb可以写在android内部。常见的task,task之间的依赖关系,以及如何插入task。
https://docs.gradle.org/current/dsl/index.html http://tools.android.com/tech-docs/new-build-system/user-guide http://stackoverflow.com/questions/20375863/difference-between-make-and-build-in-android-studio
Uncle Chen的gradle系列文章,写的不错
新闻热点
疑难解答