Android之Gradle的理解

一.前言

Gradle作爲Android官方指定的插件腳本,有必要去了解了解。這裏說說作者在學習過程中遇到難以理解的知識點。

這裏強烈推薦以下三部曲

1.groovy學習

2.gradle學習

3.來龍去脈

一定要先去認真拜讀這三篇,結合android項目去學習,相信掌握的也差不多了,存在的疑惑點興許從我這篇文章能找到你想要的答案。

二.自定義任務

Gradle中的Task要麼是由不同的Plugin引入的,要麼是我們自己在build.gradle文件中直接創建的

在當前工程中定義Task類型,只能應用在當前module中,沒什麼卵用。

下面是全局可用:

在單獨的項目中定義Task類型項目中存在大量的自定義Task類型時,在另外的一個gradle文件中定義這些Task,然後再apply到build.gradle文件中。

all.gradle:

task helloWorld << {

   println description
}

可以參考印象筆記的demo https://github.com/evernote/evernote-sdk-android

一個Task除了執行操作之外,還可以包含多個Property,其中有Gradle爲每個Task默認定義了一些Property,比如description,logger等。另外,每一個特定的Task類型還可以含有特定的Property,比如Copy的from和to等。當然,我們還可以動態地向Task中加入額外的Property。在執行一個Task之前,我們通常都需要先設定Property的值,如下:初始化task的helloworld的默認屬性description

helloWorld.configure {
   description = "this is helloWorld" 
}

三.自定義屬性property

Gradle還爲我們提供了多種方法來自定義Project的Property。
在build.gradle文件中定義Property
添加一個名爲property1的Property:
ext.property1 = "this is property1"
或者採用閉包的形式
ext {
   property2 = "this is property2"
}

使用的時候並可以不用加ext直接使用屬性

    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion
    defaultConfig {
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0"

    }

注意:rootProject.ext.android表示是在rootProject下的xxxx.gradle中定義了屬性爲android 如項目下增加config.gradle

config.gradle:

ext {
   //這是一個map屬性
    android = [compileSdkVersion: 21,
               buildToolsVersion: "25.0.2",
               applicationId    : "com.wangzhi.MaMaHelp",
               minSdkVersion    : 14,
               targetSdkVersion : 21,
               versionCode      : 264,//221
               versionName      : "7.5.53",
               environment      : false //打包時,正式環境爲false ,打alpha、beta、正式三個包時爲true
    ]

}

在項目下的build.gradle中添加如下就可以加載了:

apply from: "config.gradle"//使用config.gradle文件(就是一個groovy類)調用apply()

四.生命週期hook

有兩種方式可以編寫回調(hook)生命週期事件:
  1.將代碼寫在閉包中
  2.實現Gradle API所提供的監聽器接口
具體Gradle提供了哪些鉤子可以通過官方文檔API查看,下面使用與task執行圖有關的鉤子來學習生命週期鉤子的使用。

Task執行圖project.gradle.taskGraph
在配置階段,gradle決定了在執行階段要運行的task順序。這個順序被建模爲一個表示依賴關係的有向無環圖(DAG)。圖中的每個Task被稱爲一個節點,且該圖沒有閉環,也就是說先前執行的task不會再次執行。
Gradle API對task執行圖有對應的領域模型對象,可以通過project.gradle.taskGraph 來訪問task執行圖對象。

方法一:通過action hook

WhenReady:hook點
調用taskGraph的whenReady(Closure)方法,可以在taskGraph生成完成(也就是有向無環圖構建完成)之後執行該方法傳入的閉包。
gradle.taskGraph.whenReady{ TaskExecutionGraph taskGraph -> //當任務準備好之後會執行這個action
if(taskGraph.hasTask(release)) { //查看taskGraph中是否有發佈任務
    if(!version.release){
        version.release = true
        ant.propertyfile(file: versionFile) {
            entry(key: 'release',type:'string' , operation: '=' , value:'true')
        }
    }
}
}

方法二:通過實現TaskExecutionGraphListener監聽起監聽生命週期

實現taskGraph監聽器來掛接
通過監聽器掛接到生命週期需要兩步:
編寫一個類來實現特定的監聽器接口
用於監聽taskGraph事件的接口是由TaskExecutionGraphListener接口提供的,其他的事件監聽接口不列舉,用到的時候查閱API。
需要實現TaskExecutionGraphListener接口的graphPopulated(TaskExecutionGraph)方法,具體用法如下:

class ReleaseVersionListener implements TaskExecutionGraphListener{
final static String releaseTaskPath = ':release'
@Override
void graphPopulated(TaskExecutionGraph taskGraph){
    if(taskGraph.hasTask(releaseTaskPath)){ //確定release task是否包含在taskGraph中
        List<Task> allTasks = taskGraph.allTasks
        Task releaseTask = allTask.find{it.path == releaseTaskPath}
        Project project = releaseTask.project //通過task來訪問project
        if(!project.version.release){
            project.version.release = true
            project.ant.propertyfile(file: project.versionFile) {
                entry(key: 'release',type:'string' , operation: '=' , value:'true')
            }
        }
    }
}
}
在監聽器中不能直接訪問Project實例,但是可以通過GradleAPI,通過task來訪問該task所屬的project。註冊監聽器實現使用project.gradle.addListener(listener: Object)方法或者

project.gradle.taskGraph.addTaskExecutionGraphListener(listener: TaskExecutionGraphListener) 來註冊監聽器,

代碼如下:.gradle.taskGraph.addTaskExecutionGraphListener(new TaskExecutionGraphListener())

五.gradle執行流程

gradle就是腳本文件,需要觸發命令(如 gradle taskName等)會從上到下執行,只是執行之前gradle要先構建自身(得到gradle實例)--包裝命令中的參數---初始化settings.gradle---配置(各個項目中的build.gradle)----執行執行流程settings.gradle --->項目的build.gradle--->app.gradle(會先執行app的所有依賴(包括自定義的plugins)再執行app.build的配置)settings.gradle:用來指定哪些 module 參與構建
第一:gradle構建自身:gradle實例化
    
public class DefaultGradleLauncherFactory implements GradleLauncherFactory {
    ......
    private DefaultGradleLauncher doNewInstance(StartParameter startParameter, GradleLauncher parent,
                                                BuildCancellationToken cancellationToken, BuildRequestMetaData requestMetaData, BuildEventConsumer buildEventConsumer, final BuildSessionScopeServices sessionScopeServices, List<?> servicesToStop) {
        BuildScopeServices serviceRegistry = BuildScopeServices.forSession(sessionScopeServices);
        ......
        //Gradle框架自身初始化OK以後第一次調用時parent爲null。
        GradleInternal parentBuild = parent == null ? null : parent.getGradle();
        //創建一個DefaultGradle對象,也就是Gradle的實現,其中最重要的參數就是startParameter,包含了我們執行gradle命令時攜帶的參數。
        GradleInternal gradle = serviceRegistry.get(Instantiator.class).newInstance(DefaultGradle.class, parentBuild, startParameter, serviceRegistry.get(ServiceRegistryFactory.class));
        //把實例化的Gradle對象傳入DefaultGradleLauncher實例。
        DefaultGradleLauncher gradleLauncher = new DefaultGradleLauncher(
            gradle,
            ......
        );
        nestedBuildFactory.setParent(gradleLauncher);
        return gradleLauncher;
    }
    ......
}

第二:構建Settings對象和Project對象
 NotifyingSettingsLoader.java
 
public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
        //Settings對象創建
        SettingsInternal settings = settingsLoader.findAndLoadSettings(gradle);
        ......
		//projects對象創建是通過Settings完成的:settings.getDefaultProject()
        buildLoader.load(settings.getRootProject(), settings.getDefaultProject(), gradle, settings.getRootClassLoaderScope());
        gradle.getBuildListenerBroadcaster().projectsLoaded(gradle);
        return settings;
    }
第三:根據Stags.xxxx枚舉實例,進行init、configure、build

//此時upTo參數值爲枚舉的Stage.Build
private void doBuildStages(Stage upTo) {
    if (stage == Stage.Build) {
        //狀態控制,避免多次狀態build,第一次執行該方法時stage爲null。
        throw new IllegalStateException("Cannot build with GradleLauncher multiple times");
    }
    //第一次實例化調用該方法時stage=null,所以構建生命週期進入Load狀態(初始化階段)。
    if (stage == null) {
        // Evaluate init scripts
        initScriptHandler.executeScripts(gradle);


        // Build `buildSrc`, load settings.gradle, and construct composite (if appropriate)
        settings = settingsLoader.findAndLoadSettings(gradle);
        //標記當前狀態爲Load狀態(初始化階段)。
        stage = Stage.Load;
    }


    if (upTo == Stage.Load) {
        return;
    }


    if (stage == Stage.Load) {
        //如果當前stage是Load狀態(初始化階段),則接着構建生命週期進入Configure狀態(配置階段)。
        // Configure build
        buildOperationExecutor.run("Configure build", new ConfigureBuildAction());
        //標記當前狀態爲Configure狀態(配置階段)。
        stage = Stage.Configure;
    }


    if (upTo == Stage.Configure) {
        return;
    }
    //Load狀態(初始化階段)和Configure狀態(配置階段)完後進入Build狀態(執行階段)。
    // After this point, the GradleLauncher cannot be reused
    stage = Stage.Build;


    // Populate task graph
    buildOperationExecutor.run("Calculate task graph", new CalculateTaskGraphAction());


    // Execute build
    buildOperationExecutor.run("Run tasks", new RunTasksAction());
}
圍繞整個 Gradle 構建過程的核心是幾個委託實例對象:
      構建類型             生成的委託實例對象
Build script          Project
Init script              Gradle                一個項目只有一個
Settings script         Settings              一個項目只有一個 
編寫 Gradle 腳本時獲取 Gradle 實例後進行的設置其實都是在對該對象進行設置的
編寫 Gradle 腳本時獲取 Settings 實例後進行的設置其實都是在對該對象進行設置
編寫 Gradle 腳本時獲取對應不同的 Project 實例後進行的設置其實都是在對該對象進行設置

六.自定義插件

自定義插件:https://juejin.im/entry/577bc26e165abd005530ead8

在 Android Studio 中 Maven 倉庫相關的概念和應用。
  Maven 包
  Maven 倉庫
  發佈包到本地倉庫
  發佈包到 Bintray Jcenter 遠程倉庫
  發佈包到 Sonatype MavenCentral 遠程倉庫
  搭建私有 Sonatype 倉庫
  搭建私有 Artifacotory 倉庫
對於 Android 開發者而言,只需要知道 Maven 是一種構建工具,Maven 包是由所謂 POM(Project Object Model)所定義的文件包格式即可。
Gradle 可以使用 Maven 包,而且大部分的 Android 能夠使用的遠程依賴包都是 Maven 包。
對於一個合符規範的 Maven Package,pom 文件、aar(或者 jar) 文件是必須的。
maven包:可以代表一個jar,此生成這個jar的lib可能會依賴很多jar包,這些依賴都是由POM其實是個xml來定義依賴和格式的。比如規定groupId/articleId/versiong以及託管在maven的是aar還是jar包形式
Maven 倉庫:Maven 包集中存放的地方
這些倉庫,可以是放在本地,也可以放在某個遠程服務器上。 可以是私有倉庫,也可以是公開的。下面是筆者日常開發用的庫列表:
mavenCentral();//mavenCentral 是最早的 maven 中央倉庫
jcenter()//jcenter 是 Android Studio 0.8 版本起的默認 maven 中央倉庫
maven {//是筆者的本機的倉庫
     url 'file:///Users/my-user-name/Documents/Android/repo/'
}
maven {//筆者部署在內網服務器的私有倉庫
     url 'http://192.168.99.100:8081/content/repositories/releases/'
}
maven { //是可以把 github 項目發不成 maven 庫(jitpack 本身是一個很酷的 idea)
    url "https://jitpack.io" 
}
maven { url : xxx},這種格式可以配置任何一個存在的倉庫;只要將包製作成符合maven格式的包,就可以將包放在任何地方,只需要使用maven { url : xxx}格式就可以使用包

一個符合規範的 maven 包至少包含 pom 文件和主文件包。難道這些都要手動編寫和創建麼?

除了官方的插件之外,我們可以自定義自己的插件,實現簡單邏輯:

1.打包插件

  插件是一個類,繼承自 org.gradle.api.Plugin 接口,重載 void apply(Project project) 方法,這個方法將會傳入使用這個插件的 project 的實例,這是一個重要的 context。
  (1) 首先,你得新建一個Android Project
  (2) 然後再新建一個Module,這個Module用於開發Gradle插件,同樣,Module裏面沒有gradle plugin給你選,但是我們只是需要一個“容器”來容納我們寫的插件,因此,你可以隨便選擇一個Module類型(如Phone&Tablet Module或Android Librarty),因爲接下來一步我們是將裏面的大部分內容刪除,所以選擇哪個類型的Module不重要。
  (3) 將Module裏面的內容刪除,只保留build.gradle文件和src/main目錄。
      由於gradle是基於groovy,因此,我們開發的gradle插件相當於一個groovy項目。所以需要在main目錄下新建groovy目錄
  (4) groovy又是基於Java,因此,接下來創建groovy的過程跟創建java很類似。在groovy新建包名,如:com.hc.plugin,然         後在該包下新建groovy文件,通過new->file->MyPlugin.groovy來新建名爲MyPlugin的groovy文件。
  (5) 爲了讓我們的groovy類申明爲gradle的插件,新建的groovy需要實現org.gradle.api.Plugin接口。
  (6) 現在,我們已經定義好了自己的gradle插件類,接下來就是告訴gradle,哪一個是我們自定義的插件類,因此,需要在               main目錄下新建resources目錄,然後在resources目錄裏面再新建META-INF目錄,再在META-INF裏面新建gradle-                 plugins目錄。最後在gradle-plugins目錄裏面新建properties文件,注意這個文件的命名,你可以隨意取名,但是後面使          用這個插件的時候,會用到這個名字。比如,你取名爲com.hc.gradle.properties,而在其他build.gradle文件中使用自定          義的插件時候則需寫成:apply plugin: 'com.hc.gradle'
      使用插件方式二:import 插件所在類,然後apply plugin 插件名
  (7) 因爲我們要用到groovy以及後面打包要用到maven,所以在我們自定義的Module下的build.gradle需要添加如下代碼:
      apply plugin: 'groovy'
      apply plugin: 'maven'

      dependencies {
        compile gradleApi()//gradle sdk
    compile localGroovy()//groovy SDK
}
repositories {
       mavenCentral()

     }

2.當前項目的插件

前面我們講了如何自定義gradle插件並且打包出去,可能步驟比較多。有時候,你可能並不需要打包出去,只是在這一個項目中使用而已,那麼你無需打包這個過程。
只是針對當前項目開發的Gradle插件相對較簡單。步驟之前所提到的很類似,只是有幾點需要注意:
1.新建的Module名稱必須爲BuildSrc 注意setting.build中不需要include這個Module

2.無需resources目錄,直接使用的module中apply plugin "包名.自定義的插件"

   如:apply plugin:com.hc.second.SecondPlugin

當前build.gradle中定義使用:

class ApkDistExtension {
    Closure nameMap = null;
    String destDir = null;
}


class ApkDistPlugin implements Plugin<Project> {


    @Override
    void apply(Project project) {


        project.extensions.create('apkdistconf', ApkDistExtension);//創建一個ApkDistExtension實例爲apkdistconf,這個實例必須和聲明的一致


        project.task(name:'apkdist' , type: project.tasks.HelloWorld) << {//如果不依賴,則需要單獨指定運行這個apkdist任務 :./gradle -p /app apkdist,否則這個task不會被執行,所以自定義的任務要繼承check、clean、build等任務纔會被執行
            println 'hello, world!'


            def closure = project['apkdistconf'].nameMap;
            closure('wow!');


            println project['apkdistconf'].destDir
        }
    }
}


apply plugin: ApkDistPlugin


apkdistconf {//創建是會執行這裏初始化操作
    println 'init,hello' 
    nameMap { name ->
        println 'hello, ' + name
        return name
    }
    destDir 'your-distribution-directory'
}

七.跳躍式筆記

Gradle是一種聲明式的構建工具。Gradle的DSL只是Groovy語言的內部DSL,也必須遵循Groovy的語法規則。

Gradle本身的領域對象主要有Project和Task。
Project爲Task提供了執行上下文,所有的Plugin要麼向Project中添加用於配置的Property,要麼向Project中添加不同的Task。
一個Task表示一個邏輯上較爲獨立的執行過程,比如編譯Java源代碼,拷貝文件,打包Jar文件,甚至可以是執行一個系統命令或者調用Ant。
另外,一個Task可以讀取和設置Project的Property以完成特定的操作。是不是很屌的樣子。

task關鍵字其實是一個groovy中方法的調用,該方法屬於Project,而大括號之間的內容則表示傳遞給task()方法的一個閉包。

無論在哪個 Gradle 工程模塊的腳本中打印 gradle 或者 getGradle() 對象的 hashCode 都是同一個對象,而一個 settings.gradle 一 一對應一個 Settings 實例對象,一個 project module 的 build.gradle 一 一對應一個 Project 對象。
所以我們在編寫 Gradle 腳本時要時刻銘記這個分析結論,因爲記住這個結論對於我們 Gradle 腳本的編寫和理解非常重要,很多人寫不明白 Gradle 腳本的實質其實就是分不清哪個對象有啥 API,哪個 API 屬於哪個層次的對象。

gradle中定義的buildScript、dependencies、apply、sourceSets等等關鍵字都是方法的調用
如:學習總結,環境搭建
在使用Gradle時,我們並沒有像上面的parent.configChild()一樣指明方法調用的對象,而是在build.gradle文件中直接調用task(),apply()和configuration()等方法。這是因爲在沒有說明調用對象的情況下,Gradle會自動將調用對象設置成當前Project。
比如調用apply()方法和調用project.apply()方法的效果是一樣的。查查Gradle的Project文檔,你會發現這些方法都是Project類的方法。
對於configurations()方法,該方法實際上會將所跟閉包的delegate設置成ConfigurationContainer,然後在該ConfigurationContainer上執行閉包中的代碼。再比如,dependencies()方法,該方法會將所跟閉包的delegate設置成DependencyHandler。

Gradle最常用的Plugin便是java Plugin了。和其他Plugin一樣,java Plugin並沒有什麼特別的地方,只是向Project中引入了多個Task和Property。當然,
java Plugin也有比較與衆不同的地方,其中之一便是它在項目中引入了構建生命週期的概念,就像Maven一樣。但是,和Maven不同的是,Gradle的項目構建生命週期並不是Gradle的內建機制,而是由Plugin自己引入的。
一個項目總會依賴於第三方,要麼是一個第三方類庫,要麼是自己開發的另一個module
配置Gradle的Repository,就是告訴Gradle在什麼地方去獲取這些依賴

Gradle對依賴進行分組,允許編譯時使用一組依賴,運行時使用另一組依賴。每一組依賴稱爲一個Configuration,在聲明依賴時,我們實際上是在設置不同的Configuration。
要定義一個Configuration,我們可以通過以下方式完成:studio一般不需要設置,應該是有默認的,即爲classpath
configurations {
   myDependency
}
通過dependencies()方法向myDependency中加入實際的依賴項:
dependencies {
//下面的myDependency是關鍵
   myDependency 'org.apache.commons:commons-lang3:3.0'
}
//類似studio中的classpath
dependencies {
   classpath 'com.android.tools.build:gradle:1.3.0'
}
//還有 這裏的compile,testCompile
dependencies {
    compile project(':library')
    compile 'com.android.support:recyclerview-v7:22.2.1'
    compile 'com.android.support:design:22.2.1'
    compile 'com.evernote:android-intent:1.0.1'
    testCompile 'junit:junit:4.8.2' 
}
myDependency,classpath,compile,testCompile都是Configuration(一組依賴)。
除了myDependency都不使我們定義的,爲啥呢,android Plugin會自動定義compile和testCompile分別用於編譯Java源文件和編譯Java測試源文件。classpath應該是用於所有,我類推的。
Gradle還允許我們聲明對其他Project或者文件系統的依賴。
dependencies {
//library是另一個module的名字
   compile project(':library')
}
allprojects {
    repositories {
        jcenter()
    }
    //通常studio項目沒有,咱自己加的
   apply plugin: 'idea'
   task allTask << {
      println project.name
   }
}
allprojects()方法將repositories配置一次性地應用於所有的module(子Project)和root-project本身,當然也包括定義的Task,這個task配置到所有module裏面了和root-project。

subprojects()方法用於配置所有的子Project(不包含根Project)
Gradle本身只是一個架子,真正起作用的是Task和Plugin。

Groovy語言中的兩個概念,一個是Groovy中的Bean概念,一個是Groovy閉包的delegate機制。也就是說,在配置階段,將.gradle中的內容轉化生成groovy,並配置依賴關係。

在執行時,Gradle並不會一開始便順序執行build.gradle文件中的內容,而是分爲兩個階段,第一個階段是配置階段,然後纔是實際的執行階段。

配置階段:Gradle將讀取所有build.gradle文件的所有內容來配置Project和Task等,比如設置Project和Task的Property,處理Task之間的依賴關係等。

執行階段:真正執行的是groovy編譯後的字節碼

Action就是一個閉包,但閉包不特指action 
   NamedDomainObjectContainer<T>:注意這個類:這個玩意可以創建一個T的新實例,一般作爲參數的T傳遞比如Action<? super NamedDomainObjectContainer<SourceSets>>>()
   凡是有這種方法的:xxx(Action<? super NamedDomainObjectContainer<BuildType>> action){}
   gradle代碼中就可以這麼使用就有很多類似如下的:
   signingConfigs {
        Mall {
            keyAlias 'keystore'
            keyPassword '2012wangzhi'
            storeFile file('../app/keystore.keystore')
            storePassword '2012wangzhi'
        }
APP{
}
    }
    buildTypes {//調用buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action)
        debug {//生成buildTypes實例爲debug,添加到容器中,並配置閉包
            minifyEnabled false
            shrinkResources false
            manifestPlaceholders = [
                    channelValue: "DEBUG"
            ]
            signingConfig signingConfigs.Mall


            buildConfigField("String", "NetAddr", "\"alpha.\"")
            buildConfigField("boolean", "Base_Debug", "true")
            buildConfigField("String", "PUBLIC_FROM", "\"public\"")
            buildConfigField("String", "HOST_URL", "\"http://open.alpha.lmbang.com\"")
        }
        release {//生成buildTypes實例爲release,添加到容器中,並配置閉包
            minifyEnabled false
            shrinkResources false
            signingConfig signingConfigs.Mall


            if (!rootProject.ext.android.environment) {
                buildConfigField("String", "NetAddr", "\"\"")
                buildConfigField("boolean", "Base_Debug", "false")
                buildConfigField("String", "PUBLIC_FROM", "\"public\"")
                buildConfigField("String", "HOST_URL", "\"http://open.lmbang.com\"")
                buildConfigField("String", "GRADLE_CLIENT_FLAG", "\"preg\"")
            }
        }
    }
    productFlavors {//調用buildTypes(Action<? super NamedDomainObjectContainer<productFlavors>> action)
        
            Alpha {//生成productFlavors實例爲Alpha,添加到容器中,並配置閉包
                buildConfigField("String", "NetAddr", "\"alpha.\"")
                buildConfigField("boolean", "Base_Debug", "true")
                buildConfigField("String", "PUBLIC_FROM", "\"public\"")
                buildConfigField("String", "HOST_URL", "\"http://open.alpha.lmbang.com\"")
                buildConfigField("String", "GRADLE_CLIENT_FLAG", "\"preg\"")
            }
          Beta {//生成productFlavors實例爲Beta,添加到容器中,並配置閉包
                buildConfigField("String", "NetAddr", "\"beta.\"")
                buildConfigField("boolean", "Base_Debug", "true")
              buildConfigField("String", "PUBLIC_FROM", "\"public\"")
                buildConfigField("String", "HOST_URL", "\"http://open.beta.lmbang.com\"")
              buildConfigField("String", "GRADLE_CLIENT_FLAG", "\"preg\"")
            }
            Online {//生成productFlavors實例爲Online,添加到容器中,並配置閉包
                buildConfigField("String", "NetAddr", "\"\"")
                buildConfigField("boolean", "Base_Debug", "false")
                buildConfigField("String", "PUBLIC_FROM", "\"public\"")
                buildConfigField("String", "HOST_URL", "\"http://open.lmbang.com\"")
                buildConfigField("String", "GRADLE_CLIENT_FLAG", "\"preg\"")
            }
        }
   
   
   當我們使用 task myTask{ xxx}的時候。花括號是一個 closure。這會導致gradle 在創建這個 Task 之後,返回給用戶之前,會先執行 closure 的內容。
   當我們使用 task myTask << {xxx}的時候,我們創建了一個 Task 對象,同時把 closure 做爲一個 action 加到這個 Task 的 action 隊列中,並且告訴它“最後才執行這個 closure”(注意,<<符號是 doLast 的代表)。
   
   //dependsOn是一個函數,下面這句話的意思是 clean任務依賴cposCleanTask任務。所以  
   //當你gradle clean以執行clean Task的時候,cposCleanTask也會執行  
   clean.dependsOn 'cposCleanTask'  clean是插件的的任務,cposCleanTask是自定義的任務,進行依賴關係,當執行Clean任務時會先執行依賴cposCleanTask
   每一個build.gradle文件都會轉換成一個Project對象。在Gradle術語中,Project對象對應的是Build Script。
   /* 
     最關鍵的內容來了: buildTypesScriptBlock. 
     buildTypes和上面的signingConfigs,當我們在build.gradle中通過{}配置它的時候, 
     其背後的所代表的對象是NamedDomainObjectContainer<BuildType> 和 
     NamedDomainObjectContainer<SigningConfig> 
     注意,NamedDomainObjectContainer<BuildType/或者SigningConfig>是一種容器, 
     容器的元素是BuildType或者SigningConfig。我們在debug{}要填充BuildType或者 
    SigningConfig所包的元素,比如storePassword就是SigningConfig類的成員。而proguardFile等 
    是BuildType的成員。 
    那麼,爲什麼要使用NamedDomainObjectContainer這種數據結構呢?因爲往這種容器裏 
    添加元素可以採用這樣的方法: 比如signingConfig爲例 
    signingConfig{//這是一個NamedDomainObjectContainer<SigningConfig> 
       test1{//新建一個名爲test1的SigningConfig元素,然後添加到容器裏 
         //在這個花括號中設置SigningConfig的成員變量的值 
       } 
      test2{//新建一個名爲test2的SigningConfig元素,然後添加到容器裏 
         //在這個花括號中設置SigningConfig的成員變量的值 
      } 
    } 
    在buildTypes中,Android默認爲這幾個NamedDomainObjectContainer添加了 
    debug和release對應的對象。如果我們再添加別的名字的東西,那麼gradleassemble的時候 
    也會編譯這個名字的apk出來。比如,我添加一個名爲test的buildTypes,那麼gradle assemble 
    就會編譯一個xxx-test-yy.apk。在此,test就好像debug、release一樣。 
   */ 

   使用gradle命令:
   
   1.gradle tasks 查看當前項目或module已有的所有任務
   2.gradle properties 查看當前項目或module已有的可用屬性
   3.gradle -h         查看幫助文檔
   4.gradle projects 查看工程信息 會根據include的數量+1 項目本身也是一個project 一個build.gradle對應一個project


 Gradle工作包含三個階段:

首先是初始化階段。對我們前面的multi-project build而言,就是執行settings.gradle
Initiliazation phase的下一個階段是Configration階段。
Configration階段的目標是解析每個project中的build.gradle。比如multi-project build例子中,解析每個子目錄中的build.gradle。在這兩個階段之間,我們可以加一些定製化的Hook。這當然是通過API來添加的。
Configuration階段完了後,整個build的project以及內部的Task關係就確定了。恩?前面說過,一個Project包含很多Task,每個Task之間有依賴關係。Configuration會建立一個有向圖來描述Task之間的依賴關係。所以,我們可以添加一個HOOK,即當Task關係圖建立好後,執行一些操作。
最後一個階段就是執行任務了。當然,任務執行完後,我們還可以加Hook。

Gradle提供了一種名爲extra property的方法。extra property是額外屬性的意思,在第一次定義該屬性的時候需要通過ext前綴來標示它是一個額外的屬性。
定義好之後,後面的存取就不需要ext前綴了。ext屬性支持Project和Gradle對象。即Project和Gradle對象都可以設置ext屬性
如:
ext {
    android = [compileSdkVersion: 21,
               buildToolsVersion: "25.0.2",
               applicationId    : "com.wangzhi.MaMaHelp",
               minSdkVersion    : 14,
               targetSdkVersion : 21,
               versionCode      : 264,//221
               versionName      : "7.5.53",
               environment      : false //打包時,正式環境爲false ,打alpha、beta、正式三個包時爲true
    ]
}
  當一個Project apply一個gradle文件的時候,這個gradle文件會轉換成一個Script對象。這個,相信大家都已經知道了。
Script中有一個delegate對象,這個delegate默認是加載(即調用apply)它的Project對象。但是,在apply函數中,有一個from參數,還有一個to參數(參考圖31)。通過to參數,你可以把delegate對象指定爲別的東西。
delegate對象是什麼意思?當你在Script中操作一些不是Script自己定義的變量,或者函數時候,gradle會到Script的delegate對象去找,看看有沒有定義這些變量或函數。
gradle文件中包含一些所謂的Script Block(姑且這麼稱它)。Script Block作用是讓我們來配置相關的信息。不同的SB有不同的需要配置的東西。這也是我最早說的行話。比如,源碼對應的SB,就需要我們配置源碼在哪個文件夾裏


插件的源碼可以使用 Groovy、Scala、Java 三種語言,筆者不會 Scala,所以平時只是使用 Groovy 和 Java。
前者用於實現與 Gradle 構建生命週期(如 task 的依賴)有關的邏輯,後者用於核心邏輯,表現爲 Groovy 調用 Java 的代碼。

Gradle 採用了 Groovy 語言作爲主要的腳本語言。一個 build.gradle 文件,其實是一個 Groovy 類
  如settings.gradle 和 build.gradle 在 Gradle 平臺中,其實都是一個 Groovy 對象。
    Gradle 通過插件(plugin)的方式來支持構建。插件是很多任務(task)的集合,task 中又包含了許多 action。
Groovy 是一個基於 JVM 的語言,代碼最終編譯成字節碼(bytecode)在 JVM 上運行
├── app                     //app module
│   ├── build.gradle        //app module 的 build.gradle
├── build.gradle            //項目 build.gradle,通常配置項目全局配置,如 repositories 和 dependencies
├── gradle.properties       //項目屬性文件,通常可以放置一些常量
├── lib                     //lib module
│   ├── build.gradle        //lib module 的 build.gradle
└── settings.gradle         //項目總體設置,通常是配置項目中所有的 module




Gradle 的核心代碼,只提供了一個框架,具體的功能(如構建 Android 工程)是通過插件機制來實現的。
Gradle 提供了大量官方的插件,如 Maven、Groovy、Java、Publishing、Signing等,也有大量第三方的插件(Android),甚至每個人都可以自己實現一個插件(如 筆者開發的 Bugtags 插件,這個將在最後一篇講述)。
這些 plugin 定義了一系列的 task、DSL 和約定,在build.gradle 文件使用這些 plugin:
apply plugin: java 或者apply plugin: 'groovy' 引用java或者groovy插件 因爲想要使用java或groovy插件中固有的DSL(特定領域語言,顧名思義只能應用指定的地方)比如特有的屬性、方法等等使用jar包中的方法就要載入jar包
當你寫了一個獨立的 file_uri.gradle 文件,你可以通過:
apply from: 'file_uri.gradle'

Gradle最常用的Plugin便是java Plugin了。和其他Plugin一樣,java Plugin並沒有什麼特別的地方,只是向Project中引入了多個Task和Property。當然,
java Plugin也有比較與衆不同的地方,其中之一便是它在項目中引入了構建生命週期的概念,就像Maven一樣。但是,和Maven不同的是,Gradle的項目構建生命週期並不是Gradle的內建機制,而是由Plugin自己引入的。
一個項目總會依賴於第三方,要麼是一個第三方類庫,要麼是自己開發的另一個module
配置Gradle的Repository,就是告訴Gradle在什麼地方去獲取這些依賴


Gradle對依賴進行分組,允許編譯時使用一組依賴,運行時使用另一組依賴。每一組依賴稱爲一個Configuration,在聲明依賴時,我們實際上是在設置不同的Configuration。
要定義一個Configuration,我們可以通過以下方式完成:studio一般不需要設置,應該是有默認的,即爲classpath
configurations {
   myDependency
}
通過dependencies()方法向myDependency中加入實際的依賴項:
dependencies {
//下面的myDependency是關鍵
   myDependency 'org.apache.commons:commons-lang3:3.0'
}
//類似studio中的classpath
dependencies {
   classpath 'com.android.tools.build:gradle:1.3.0'
}
//還有 這裏的compile,testCompile
dependencies {
    compile project(':library')
    compile 'com.android.support:recyclerview-v7:22.2.1'
    compile 'com.android.support:design:22.2.1'
    compile 'com.evernote:android-intent:1.0.1'
    testCompile 'junit:junit:4.8.2' 
}
myDependency,classpath,compile,testCompile都是Configuration(一組依賴)。
除了myDependency都不使我們定義的,爲啥呢,android Plugin會自動定義compile和testCompile分別用於編譯Java源文件和編譯Java測試源文件。classpath應該是用於所有,我類推的。
Gradle還允許我們聲明對其他Project或者文件系統的依賴。
dependencies {
//library是另一個module的名字
   compile project(':library')
}
allprojects {
    repositories {
        jcenter()
    }
    //通常studio項目沒有,咱自己加的
   apply plugin: 'idea'
   task allTask << {
      println project.name
   }
}
allprojects()方法將repositories配置一次性地應用於所有的module(子Project)和root-project本身,當然也包括定義的Task,這個task配置到所有module裏面了和root-project。


subprojects()方法用於配置所有的子Project(不包含根Project)
Gradle本身只是一個架子,真正起作用的是Task和Plugin。

學習api文檔:

android 插件api文檔

gradle的api文檔

gradle DSL for Android

學習博客

https://juejin.im/entry/577bc26e165abd005530ead8

http://kvh.io/cn/tags/EmbraceAndroidStudio/

https://www.aliyun.com/jiaocheng/1117895.html

https://blog.csdn.net/blueangle17/article/details/60872224

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章