Gradle 入門到精通(四)

8 全局變量 ext

我們前面講解了gradle的生命週期,在配置的過程中,整個項目會生成一個gradle 對象,每個build.gradle的文檔都會生成一個project對象。這兩個對象都有一個ext,這個ext的屬性就類似於我們的錢包一樣,獨立屬於gradle與project對象。我們可以往這個ext對象裏面放置屬性。

8.1 gradle的ext對象

我們可以使用這樣的方法存儲一個變量,這個變量屬於gradle,整個工程都能使用

 gradle.ext.myName = 'helloKay'

讀取方式如下

task A{

    doLast{
         println(gradle.ext.myName)
    }

}

8.2 project 的ext對象

保存值

ext{
     myName ='abc'
     myName1 = 'abc'
}

獲取值,可以直接獲取

println(myN)

上面這個代碼println(myN)就等於println(project.ext.myN)

我們一般在ext內存儲一些通用的變量,除此以外,我們也使用這個ext來做一些很酷的功能,比如說我們的gradle文件很大了,我們可以好像代碼一下,進行抽取。

8.3 gradle的複用

新建一個other.gradle,放置在app目錄下面,在other內輸入下面的內容

println "configuring $project"

task hello{
    doLast{
        println 'hello from other script'
    }
}

def showMyName(){
    'i am a boy'
}

ext{
    showName = showMyName()
}

接着我們可以在build.gradle文件內使用這個些內容

apply from:'other.gradle'

這句話是加載other.grale,apply from是加載其他插件或者腳本的意思,我們在這裏加載了本地的腳本,之後,我們可以直接調用腳本里面的task,如果需要調用方法的話,需要通過ext對象來調用。我們常常使用公用的方法來獲取應用的信息。

如果抽取的文件在根目錄怎麼處理呢?

apply from:new File(project.getProjectDir().parent,"util.gradle")

9 BuildConfig文件

android提供了一種機制,能夠讓我們在build.gradle定義一些字段,在代碼裏面使用。假設一個場景,我們在公司開發的時候,爲了安全,我們常常提供兩個服務器,一個測試服務器,一個是線上服務器。常見的方法是我們在一個常量的類裏面提供一個boolean來做開關,如果是測試版就設置爲false,如果是線上版本就設置爲true。這樣開發確實很方便,但是可能出現一個比較嚴重的問題,就是開發人員不小心在打包測試版本的時候,沒有吧boolean設置爲ture。導致線上包出現問題。

實際gradle提供了一個工具類給我們來使用,這個類就是BuildConfig。這個類會把gradle.build的。

buildConfigField "boolean", "isdebug", "false"

這句話是定義在定製版本內部的,我們可以在不同的版本里獲取到這個值,這裏有三個參數,

  • 參數一 “boolean” 代表這個參數的類型
  • 參數二 “isdebug” 參數的名稱
  • 參數三 “false” 參數的值

注意不管你定義的什麼類型的參數

我們在代碼的時候讀取的時候按照下面的來讀取。

   if(BuildConfig.isdebug){
        Toast.makeText(this,"isDebug",0).show();
    }else{
        Toast.makeText(this,"isNotDebug",0).show();
    }

10 Task 任務

10.1 什麼是Task

我們在前面的學習了gradle的很多知識,特別是學習到構建不同版本的時候,我們在命令行中輸入了./gradlew assembleRelease 命令就能夠得到一個安裝包。但是我們沒有和大家講解這個命令是什麼。其實這個就是一個Task(任務),Task實際就是一連串的操作,最後得到我們需要的內容。

我們可以這麼理解,Gradle是一個大的舞臺,這個舞臺提供了基礎的能力。不同插件(java Pluging、android pluging)是不同的表演團隊,提供不同的表演。這個表演就是Task。不同的Task完成不同的任務,我們根據不同的需求選擇不同的Task。如果在開發中,發現系統插件給我的Task不能滿足需求怎麼辦呢?我們也可以根據規則,編寫一個屬於我們自己的Task。

所以綜合一下,Task分成了兩種,一種是Pluging提供的,另外一種是開發者自定義的。

10.2 查看項目的Tasks

我們打開android studio,再最右邊的便籤欄可以打開當前項目的所有的task。我們接着打開build標籤,

在上面的圖裏面,我們發現了之前的任務。我們可以嘗試點擊一下執行相關的任務。發現和我們在命令行執行的效果是一樣的。除了使用IDE查看任務外,我們也可以使用命令來查看當前項目的任務。

./gradlew tasks

如果想看個個任務的詳細信息

./gradlew tasks --all

詳細信息裏面多了很多內容,比如說項目的依賴等信息。

我們發現這些Task很多類似名稱的Task,這個是插件爲我們提供的任務。

  • assemble 組合項目的輸出,在java中多用於生成jar、war包,而在android中用於生成apk
  • check 用戶項目的檢查任務,比如說lint
  • connectedCheck 用於連接設備的檢查
  • build 這種任務會執行assemble與check的任務
  • clean 這個task清空項目的所有輸出。

10.3 定義Task

我們學會了查看當前項目的任務,我們來看看如何定義一個Task,及task的使用。

  • task myTask { configure closure }
  • task myTask(type: SomeType) { configure closure }

上面是task的兩種定義方式,第一種是直接定義一個task,第二種是讓這個task繼承於某類的task,可以理解爲繼承。

直接定義Task

我們可以在腳本的任意位置添加以下的代碼

task showMeTheMoney{
    println("show me the money");
}

接着我們在終端的界面輸入下面的命令

 ./gradlew showMeTheMoney
使用某種type來定義Task
task myCopy(type:Copy){
        from './build/outputs/apk/app-noLog-free-release.apk'
        into './test/'
   }

接着我們在終端的界面輸入下面的命令(注意你的目錄可能和我的不一樣,替換成你自己的目錄)

 ./gradlew myCopy   

上面的這個任務繼承於Copy。

這個是Copy的所有屬性,https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html

除了Copy以外我們還有什麼類型呢?

https://docs.gradle.org/current/userguide/java_plugin.html,我們可以參考Java插件的類型

上面是支持的所有類型,我們還可以按照文檔實現一個delete類型的task

task myCheck(type:Delete){
     delete 'test'
}

10.4 Task的關係與執行順序

我們在實際開發中會有這樣的需要,很多任務實際上都是有關聯的。比如說任務A執行的時候需要任務B執行完成,我們就會說A依賴於B,或者任務A裏面裏面的命令執行需要按照一定的順序執行,這個時候我們就需要給這個Task的指令提供一定的順序標準,簡單的來說,就是我們在運行某個task時,我們需要先執行某些命令,再執行其他命令,最後再執行特定的命令。

10.4.1 Gradle 生命週期加深

根據之前我們學習的內容,我知道Gradle的生命週期有以下幾個

  • Initialization 讀取setting.gradle文件,分辨項目是單項目結構還是多項目結構。在這個階段會生成一個全局Gradle對象
  • Configuration 讀取每個參與編譯的項目的build.gradle文件,執行所有項目的task的內容(除了doLast、doFrist以外)。並會根據task的關係組成一個有向圖。
  • Execution 根據有向圖執行Task的內容。

下面我們來看看這個例子。

settings.gradle
println 'This is executed during the initialization phase.'
build.gradle
println 'This is executed during the configuration phase.'

task configured {
    println 'This is also executed during the configuration phase.'
}

task test {
    doLast {
    println 'This is executed during the execution phase.'
    }
}

task testBoth {
    doFirst {
        println 'This is executed first during the execution phase.'
    }
    doLast {
        println 'This is executed last during the execution phase.'
    }
    println 'This is executed during the configuration phase as well.'
}

執行的結果就跟我們之前提到的內容一樣

最先在Initialization執行settings.gradle的內容,接着在Configuration執行各Task的內容,最後在
Execution中執行doFirst與doLast的內容。

10.4.2 doFirst 與 doLast

前面我們講了生命週期的問題,我們知道doFirst 與 doLast 都是在Execution階段執行的,Task的各項操作都是類似鏈條一樣執行的,doFirst會把內部的closure插入到這個鏈條的頂部,doLast會把內部的鏈條的closure插入到鏈條的底部,所以doFirst會比doLast的執行提前。

 task testBoth {
    doFirst {
        println 'This is executed first during the execution phase.'
    }
    doLast {
        println 'This is executed last during the execution phase.'
    }
    println 'This is executed during the configuration phase as well.'
 }

注意如果有比較舊的項目,你會看到這樣的寫法

task showArgs << {
    println "Hello World"
}

在這裏<<就是doLast的縮寫,注意新的版本gradle已經建議不要這麼寫了。

10.4.3 任務之前的關係

我們之前講過了,項目之間可能存在互相依賴的關係,比如說A需要B執行完成後再執行,這個時候我們可以使用任務的關係運算符,讓任務按指定的順序執行。

dependsOn 依賴關係
task A(dependsOn:'B'){

    doFirst{
         println("this is A")
    }

    }

task B{
    doFirst{
        println("this is B")
    }

}

如果執行A,會在A的執行前執行B的內容,也就是說A的執行依賴於B,dependsOn 可以多個依賴

mustRunAfter 依賴的弱前後關係
task A{

    doFirst{
         println("this is A")
        }
    }

task B{
    doFirst{
        println("this is B")
    }
}

task C{
    doFirst{
        println("this is C")
    }
}

我們有三個任務,A,B,C。它們的依賴關係是

A.dependsOn(B)
A.dependsOn(C)

上面這個意思就是告訴gradle B,C會在A前面執行。但是執行的順序一般是按照這個任務的名稱。但是我們在開發中可能會有這樣的需求,C要先執行,再執行B。好那我們再改改

A.dependsOn(B)
A.dependsOn(C)
B.dependsOn(C)

經過我們的改造,好像是可以了,但是有一個很嚴重的問題。因爲我們的任務都是一個個獨立的個體。也就是能夠單獨執行的,但是上面的關係,如果我們單獨執行B就必須執行C。怎麼做呢?我們要引進一個弱關鍵關係操作符。mustRunAfter

A.dependsOn(B)
A.dependsOn(C)
C.mustRunAfter(B)
finalizeBy 最後執行的依賴
task UITest{

    doFirst{
         println("this is UITest")
        }
    }

task Test{
    doFirst{
        println("this is Test")
    }
}

task CopyReport{
    doFirst{
        println("this is CopyReport")
    }
}

他們的關係是
UITest->Test->CopyReport,按照我們之前的學習的內容,可能有同學覺得要這樣寫

  CopyReport.dependsOn(Test)
  Test.dependsOn(UITest)

編寫完成成後,我們把命令提供給運維的哥們,我們告訴他如果要測試應用的話,需要運行CopyReport這個任務。運維的哥們看到就覺得很奇怪了,明明是測試的任務,爲啥要運行 CopyReport。爲了更好使用的話,我們引入一個新的關鍵字finalizedBy。這個關鍵字會在任務運行完成後再運行指定的任務。

  Test.dependsOn(UITest)
  Test.finalizedBy(CopyReport)
10.4.4 動態修改Task

在項目中,我們可能會需要對某些Task的關係、內容進行修改。

方法1

gradle在配置的階段會把所有的Task進行彙總,我們可以在這個階段對特定的task修改

tasks.whenTaskAdded { task ->
     if (task.name == 'assembleWithLogFreeRelease') {
        task.dependsOn(A)
    }
}

tasks.whenTaskAdded { task ->
     if (task.name == 'assembleWithLogFreeRelease') {
        task.enabled = false
    }
}
方法2

上面的方法是對特定的Task進行修改,其實我們是可以對特定的流程進修改的。gradle提供了一個對象applicationVariants。這個對象封裝了android 所有的流程,我們可以對指定的流程進行修改,這個對象的參數如下圖

 android.applicationVariants.all { variant ->
         variant.assemble.finalizedBy(A)
    }
10.4.5 task 帶參數輸入

在實際開發中,會有這樣的需求,會根據我們的輸入

// File: build.gradle
task showArgs << {
    println "$word1 $word2"
}

在終端輸入

$ gradle showArgs -Pword1=hello -pword2=world

10.5 安全打包

我們知道,我們的應用發佈的時候需要使用一個keyStroe簽名文件和簽名密碼,對於一個app來說,這個就是app的身份證,如果別人拿到了這些資料就可以重新打包應用,爲了安全,我們應該把線上包的簽名和密碼與代碼分開來保存。現在的一般做法就是把這些資源寫在配置文件,再通過腳本來修改打包。

我們一般都使用這兩個文件來保存密碼,使用的方式有點不同。

如果使用的是gradle.properties文件,直接在文件內定義變量即可

    signname = xmg
    signpasss = 123456

讀取的時候

 signingConfigs {
    debug {
    }
    realse {
        keyAlias  signname
        keyPassword signpasss
        storeFile file('/Users/kay/Desktop/release_key.jks')
        storePassword signpasss

    }
}

如果是其他的properties文件的話,按照下面的來讀取

Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def myname = properties.getProperty('name')
def pass = properties.getProperty('pass')

最後將編寫好build.gradle腳本進行打包,就可以使用。

11相關資料

11.1 android 構建的完整流程

11.2 Gradle文檔資料地址

https://gradle.org/docs

11.3 Gradle android pluging dsl 文檔地址

https://google.github.io/android-gradle-dsl/current/index.html

11.4 Gradle android pluging使用手冊

https://google.github.io/android-gradle-dsl/current/index.html

11.5 Gradle android pluging源碼

https://android.googlesource.com/platform/tools/build/+/cab495f54cd31e4e93c36e6aa4b7af661aac2357/gradle/src/main/groovy/com/android/build/gradle?autodive=0%2F

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