Gradle基礎(chǔ)到進(jìn)階——Gradle基礎(chǔ)自定義插件(三)

環(huán)境配置

  • 官方地址:https://gradle.org/releases/
  • 下載之后將gradle下面的bin目錄添加到系統(tǒng)的Path中(Android studio目錄一般都是在.gradle目錄下)


    image.png
  • 測試是否配置好:gradle -v

Hello Gradle

  • 新建一個(gè)build.gradle
println("Hello Gradle")
  • cmd定位到新建文件目錄下
gradle help
  • 查看輸出結(jié)果


    image.png
  • build.gradle是構(gòu)建Project的核心文件,也是入口:
    • 如果沒有該文件,會(huì)出現(xiàn)not found in root project 'xxxxx' 提示異常。
    • 必須要有一個(gè)可以運(yùn)行的task,運(yùn)行后自動(dòng)生成.gradle文件夾下的內(nèi)容

gradle wrapper

  • Gradle Wrapper用來配置開發(fā)過程中用到的Gradle構(gòu)建工具版本。避免因?yàn)镚radle不統(tǒng)一帶來的不必要的問題
  • 在工程目錄下使用cmd命令生成wrapper:
gradle wrapper
  • 標(biāo)準(zhǔn)的gradle工程目錄
    • gradlew和gradlew.bat分別是Linux和Windows下的可執(zhí)行腳本
    • 具體業(yè)務(wù)邏輯是在/gradle/wrapper/gradle-wrapper.jar中實(shí)現(xiàn)
    • gradlew最終還是使用Java執(zhí)行這個(gè)jar包來執(zhí)行相關(guān)的Gradle操作


      image.png

gradle-wrapper.properties

image.png
  • distributionBase:下載的Gradle壓縮包解壓后存儲(chǔ)的主目錄
  • distributionPath:相對于distributionBase的解壓后的Gradle壓縮包的路徑
  • distributionUrl:Gradle發(fā)行版壓縮包的下載地址
    • bin:二進(jìn)制發(fā)布版。
    • all:bin基礎(chǔ)上還包含了源碼和文檔。
  • zipStoreBase:同distributionBase,只不過存放的是zip壓縮包的
  • zipStorePath:同distributionPath,只不過存放的是zip壓縮包的

gradle構(gòu)建機(jī)制

android gradle工程目錄

image.png

Gradle DSL

  • DSL(Domain Specific Language) 領(lǐng)域特定語言,或領(lǐng)域?qū)僬Z言。簡單來說就是專門關(guān)注某一領(lǐng)域的語言,它在于專而不是全,最典型的比如HTML。
  • Gradle可以使用Groovy DSL,專門用來開發(fā)Gradle的構(gòu)建腳本。所以說Gradle整體設(shè)計(jì)是以作為一種語言為導(dǎo)向的,并非成為一個(gè)嚴(yán)格死板的框架

settings.gradle

image.png

  • Gradle支持多工程構(gòu)建,使用settings.gradle來配置添加子工程(模塊)。
  • settings文件在初始化階段執(zhí)行,創(chuàng)建Settings對象,在執(zhí)行腳本時(shí)調(diào)用該對象的方法。
  • Settings.include(String... projectPaths):
    • 將給定的目錄添加到項(xiàng)目構(gòu)建中,':app'表示文件相對路徑,相當(dāng)于'./app'文件夾。
    • 多項(xiàng)目架構(gòu)進(jìn)行分層,把同層次的子工程放在同一文件夾下便于管理,使用':xxx:yyy'表示

build.gradle

  • build.gradle是項(xiàng)目構(gòu)建文件,每個(gè)工程都有一個(gè)build.gradle文件。
  • build.gradle在配置階段執(zhí)行,并創(chuàng)建相應(yīng)工程的Project對象,執(zhí)行的代碼可以直接調(diào)用該對象提供的方法或?qū)傩?/li>

Daemon(守護(hù)進(jìn)程)

  • 項(xiàng)目啟動(dòng)時(shí),會(huì)開啟一個(gè)client,然后啟動(dòng)一個(gè)daemon,通過client向daemon收發(fā)請求,項(xiàng)目關(guān)閉,client關(guān)閉,daemon保持啟動(dòng),有類似項(xiàng)目再次部署時(shí),會(huì)直接通過新的client訪問已經(jīng)啟動(dòng)的daemon,所以速度很快,默認(rèn)daemon不使用3小時(shí)后關(guān)閉;不同項(xiàng)目兼容性考慮,也可使用--no-daemon 啟動(dòng)項(xiàng)目,就沒有速度優(yōu)勢了
  • 手動(dòng)停止daemon:gradle wrapper --stop

生命周期

  • Initialization
    • Gradle支持單項(xiàng)目和多項(xiàng)目構(gòu)建。在初始化階段,Gradle確定哪些項(xiàng)目參與構(gòu)建,并為每個(gè)項(xiàng)目創(chuàng)建project實(shí)例,比如解析setting.gradle
  • Configuration
    • 配置階段,解析每個(gè)工程的build.gradle文件,創(chuàng)建要執(zhí)行的任務(wù)子集和確定各種任務(wù)之間的關(guān)系,并對任務(wù)做一些初始化配置
    • 解析過程中并不會(huì)執(zhí)行各個(gè)build.gradle中的task
    • 經(jīng)過Configration階段,Project之間及內(nèi)部Task之間的關(guān)系就確定了
    • 一個(gè)Project包含很多Task,每個(gè)Task之間有依賴關(guān)系。Configuration會(huì)建立一個(gè)有向圖來描述Task之間的依賴關(guān)系
    • 所有Project配置完成后,會(huì)有一個(gè)回調(diào)project.afterEvaluate(),表示所有的模塊都已經(jīng)配置完了
  • Execution
    • 運(yùn)行階段,Gradle根據(jù)配置階段創(chuàng)建和配置要執(zhí)行的任務(wù)子集,執(zhí)行任務(wù)

執(zhí)行流程

image.png

自定義任務(wù)

task

  • task是gradle中最小的任務(wù)單元,任務(wù)之間可以進(jìn)行復(fù)雜的操作(如動(dòng)態(tài)創(chuàng)建任務(wù),多任務(wù)間依賴調(diào)用等等)。gradle的執(zhí)行其實(shí)就是由各種任務(wù)組合執(zhí)行,來對項(xiàng)目進(jìn)行構(gòu)建的
  • gradlew tasks -all命令查看所有任務(wù)
  • gradlew A B 命令表示執(zhí)行任務(wù)A和B

自定義任務(wù)

  • build.gradle中自定義任務(wù)
    • task(任務(wù)名){}


      image.png
  • { }執(zhí)行的是配置階段的代碼,執(zhí)行階段要處理的邏輯需要調(diào)用doFirst、doLast方法,在閉包中實(shí)現(xiàn)。
    • doFirst{}表示任務(wù)執(zhí)行開始時(shí)調(diào)用的方法
    • doLast{}表示任務(wù)執(zhí)行結(jié)束調(diào)用的方法。
  • task A(dependsOn:[B]){} 表示任務(wù)A依賴于任務(wù)B,那么B執(zhí)行在A之前。
task A {
    println("Hello A")
    doLast {
        println("doLast A")
    }
}
task B {
    println("Hello B")
    doLast {
        println("doLast B")
    }
}

task test(dependsOn: [A, B]) {
    doLast {
        println("doLast test")
    }
}
A.dependsOn(B)
  • 自定義的任務(wù)默認(rèn)分組到other中。

DefaultTask

  • task定義的任務(wù)其實(shí)就是DefaultTask的一種具體實(shí)現(xiàn)類的對象。
  • 可以使用自定義類繼承DeaflutTask:
    • 在方法上使用@TaskAction注解,表示任務(wù)運(yùn)行時(shí)調(diào)用的方法。
    • 使用@Input表示對任務(wù)的輸入?yún)?shù)。
    • 使用@OutputFile表示任務(wù)輸出文件。
    • 使用inputs,outputs直接設(shè)置任務(wù)輸入/輸出項(xiàng)。
    • 一個(gè)任務(wù)的輸出項(xiàng)可以作為另一個(gè)任務(wù)的輸入項(xiàng) (隱式依賴關(guān)系)。

獲取任務(wù)的類型

task A {

}
println(A.class)//class org.gradle.api.DefaultTask_Decorated

自定義任務(wù)類型實(shí)現(xiàn)自定義任務(wù)

  • 創(chuàng)建一個(gè)自定義任務(wù)


    image.png
  • 自定義任務(wù)實(shí)現(xiàn)文件的輸入輸出
class MyTask extends DefaultTask {

    String from
    File out

    @TaskAction
    void method() {
        println "我是自定義任務(wù)"
        //文件進(jìn)行操作
        println inputs.files.singleFile
        def inFile = inputs.files.singleFile
         //將build.gradle文件復(fù)制到test文件
        def outFile = outputs.files.singleFile
        outFile.createNewFile()
        outFile.text = inFile.text
    }
}

task myTask(type: MyTask) {
    from = "I am Peakmain"
    out = file("test.txt")
    inputs.file file('build.gradle')
    outputs.file file('test.txt')
}

細(xì)心的人可能會(huì)發(fā)現(xiàn),我們執(zhí)行多次的時(shí)候并沒有打印,這是因?yàn)槲覀兊膬?nèi)容沒有改變,所以會(huì)直接跳過@TaskAction方法,如果我們想每次都執(zhí)行TaskAction怎么解決,我們可以在構(gòu)造函數(shù)中添加一行代碼

    MyTask() {
        outputs.upToDateWhen {
            false
        }
    }

鉤子函數(shù)

初始化階段
android中的settings.gradle中主要是gradle的初始化,具體大家看上面Gradle執(zhí)行流程圖

gradle.projectsLoaded {
    println "gradle.projectsLoaded"
}
gradle.settingsEvaluated {
    println "gradle.settingsEvaluated"
}

Terminal中執(zhí)行命令:gradlew help,我們便可以看到我們的打印


image.png

配置階段
gradle對項(xiàng)目所有l(wèi)ibrary生效,project只對當(dāng)前l(fā)ibrary生效

gradle.beforeProject {
    println "gradle.beforeProject"
}

gradle.afterProject {
    println "gradle.afterProject"
}
 //所有配置完成之后執(zhí)行
gradle.taskGraph.whenReady {
    println "gradle.taskGraph.whenReady"
}
//只對當(dāng)前有效
project.beforeEvaluate {
 //root無效
    println "beforeEvaluate"
}
project.afterEvaluate {
    println "afterEvaluate"
}

結(jié)果


image.png

gradle設(shè)置監(jiān)聽

//為gradle設(shè)置監(jiān)聽
gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {
    @Override
    void beforeEvaluate(Project project) {
        println "beforeEvaluate"
    }

    @Override
    void afterEvaluate(Project project, ProjectState projectState) {
        println "afterEvaluate"
    }
})
//設(shè)置構(gòu)建監(jiān)聽
gradle.addBuildListener(new BuildListener(){

    @Override
    void buildStarted(Gradle gradle) {

    }

    @Override
    void settingsEvaluated(Settings settings) {

    }

    @Override
    void projectsLoaded(Gradle gradle) {

    }

    @Override
    void projectsEvaluated(Gradle gradle) {

    }

    @Override
    void buildFinished(BuildResult buildResult) {

    }
})

Project

  • build.gradle在配置階段會(huì)生成project實(shí)例,在build.gradle中直接調(diào)用方法或?qū)傩?,?shí)則是調(diào)用當(dāng)前工程的project對象的方法或?qū)傩?/li>
  • 使用Project提供的api,更方便在多項(xiàng)目構(gòu)建設(shè)置
    • project(":app"){}指定的project(這里是app)配置
    • allprojects{}所有的project配置
    • subprojects{}所有的子project 配置
    • buildscript{}此項(xiàng)目配置構(gòu)建腳本類路徑
image.png

屬性擴(kuò)展

  • 使用ext對任意對象屬性進(jìn)行擴(kuò)展
    • 對project進(jìn)行使用ext進(jìn)行屬性擴(kuò)展,對所有子project可見
    • 一般在root project進(jìn)行ext屬性擴(kuò)展,為子工程提供復(fù)用屬性,通過rootProject直接訪問
    • 任意對象都可以使用ext來添加屬性:使用閉包,在閉包定義擴(kuò)展屬性。直接使用=賦值,添加擴(kuò)展屬性
    • 由誰進(jìn)行ext調(diào)用,就屬于誰的擴(kuò)展屬性
    • 在build.gradle中,默認(rèn)當(dāng)前工程的project對象,所以在build.gradle直接使用"ext="或者"ext{}"其實(shí)就是給project定義擴(kuò)展屬性
  • 使用gradle.properties以鍵值對形式定義屬性,所有project可直接使用
ext{//相當(dāng)于project.ext
    author="peakmain"
}
task myTask{
}
myTask.ext.username="peakmain"
println(myTask.ext.username)
println(myTask.username)

Gradle插件

腳本插件

afterEvaluate {
    println tasks.getByName("packageDebug")
    task zip(type: Zip) {
        archiveName "outputs.zip"http:// 輸出的文件名字
        destinationDir file("${buildDir}/custom")// 輸出的文件存放的文件夾
        from tasks.getByName("packageDebug").outputs.files// 輸入的文件
        tasks.getByName("packageDebug").outputs.files.each {
            println it
        }
    }
}

二進(jìn)制插件

class MyTask implements Plugin<Project>{

    @Override
    void apply(Project project) {
            println("MyTask")
    }
}
apply plugin:MyTask

自定義Gradle插件,實(shí)現(xiàn)packageDebug的打包

1.新建buildSrc目錄
2.新建一個(gè)build.gradle,同步構(gòu)建一下
3.新建文件夾src/main/java
4.build.gradle引入插件

apply plugin:"java"

5.新建PeakmainPlugin.java類

public class PeakmainPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        project.afterEvaluate(it -> {
            System.out.println(it.getTasks().getByName("packageDebug"));
            Map<String, Class<?>> typeMap = new HashMap<>();
            typeMap.put("type", Zip.class);
            Zip zipTask = (Zip) project.task(typeMap, "zipDebug");
            zipTask.setArchiveName("outputs.zip");
            zipTask.setDestinationDir(new File(project.getBuildDir().getAbsolutePath() + "/custom"));
            zipTask.from(it.getTasks().getByName("packageDebug").getOutputs().getFiles());
        });
    }
}

6.app目錄下的build.gradle引入插件,同步一下

apply plugin:PeakmainPlugin

7.AndroidStudio右邊的Gradle的Other多了一個(gè)zipDebug


image.png

8.結(jié)果


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容