本文以前文寫的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系列文章,寫的不錯
新聞熱點
疑難解答