Gradle系列(四) Gradle外掛

瀟風寒月發表於2019-12-22

1. 前言

Gradle系列已完成,專注於Gradle,有如下幾篇文章

依賴apply plugin: 'com.android.application'就是依賴了安卓的應用程式外掛.然後這個外掛裡面有android擴充套件,在官方文件裡面有詳細描述.但是,有時候不得不自己寫一個外掛,方便與業務開展.比如我覺得美團的熱修復,在每個方法前面插邏輯的話,肯定得插樁,插樁就得自己寫外掛.方便快捷.Gradle+ASM可以插樁.有興趣的可以去了解.

demo地址: github.com/xfhy/Gradle…

2. 簡單外掛

新建一個簡單的專案,然後建立一個buildSrc這個名字的module,這個module的名稱必須為buildSrc.因為我們建立的這個module是AS專門用來寫外掛的,會自動參與編譯.建立好之後刪除Android那一堆東西,什麼java程式碼,res,清單檔案等.只剩下build.gradle和.gitignore

QQleEj.png

把build.gradle檔案內容改成

repositories {
    google()
    jcenter()
}
apply {
    plugin 'groovy'
    plugin 'java-gradle-plugin'
}
dependencies {
    implementation gradleApi()
    implementation localGroovy()
    implementation "commons-io:commons-io:2.6"
}
複製程式碼

然後在main下面建立資料夾groovy,sync一下.沒啥問題的話,應該能編譯過.然後在groovy資料夾下面建立包名com.xfhy.plugin,然後建立一個外掛,名字叫CustomPlugin.groovy

package com.xfhy.plugin

import org.gradle.api.Plugin
import org.gradle.api.Project

class CustomPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        project.task('showCustomPlugin') {
            doLast {
                println('hello world plugin!')
            }
        }
    }
}
複製程式碼

我在這裡宣告瞭一個CustomPlugin外掛,然後裡面建立了一個task名字叫showCustomPlugin,showCustomPlugin外掛就只是簡單輸出一句話. 然後在app的build.gradle裡面引入這個外掛

import com.xfhy.plugin.CustomPlugin
apply plugin: CustomPlugin
複製程式碼

然後就可以在命令列輸入gradlew showCustomPlugin執行這個外掛.然後就可以在命令列看到輸出了.就這樣簡單的幾步,一個簡單的外掛就寫完了,雖然只是簡單地列印了hello world..

3. 外掛 plus

但是,我們肯定不會滿足於只會輸出hello world的外掛,我們來做一個稍微有點用的外掛.正如我們平常在android{} 閉包裡面,我們寫了很多次的

compileSdkVersion Config.compileSdkVersion
buildToolsVersion Config.buildToolsVersion
....
複製程式碼

那我們就來做這樣一個外掛,讀取這些配置資訊的資料.首先在包名com.xfhy.plugin下面建立AndroidExtension.groovy,它是一個bean物件,用來儲存資訊的.定義如下:

package com.xfhy.plugin

class AndroidExtension {
    String compileSdkVersion = ''
    String buildToolsVersion = ''
    String applicationId = ''
    String minSdkVersion = ''
}
複製程式碼

然後建立一個新的外掛MyAndroidPlugin.groovy

package com.xfhy.plugin

import org.gradle.api.Plugin
import org.gradle.api.Project

class MyAndroidPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        def extension = project.extensions.create('testExtension', AndroidExtension)
        project.task('AndroidPlugin') {
            doLast {
                println("minSdkVersion = ${extension.minSdkVersion}")
                println("applicationId = ${extension.applicationId}")
                println("compileSdkVersion = ${extension.compileSdkVersion}")
                println("buildToolsVersion = ${extension.buildToolsVersion}")
            }
        }
    }
}

複製程式碼

通過project.extensions.create來獲取testExtension閉包中的內容,並通過反射將閉包中的內容轉成一個AndroidExtension物件.然後我建立了一個task去讀取它.

然後我們在app下面的build.gradle中加入程式碼:

testExtension {
    minSdkVersion '22'
    applicationId 'com.xfhy.gradledemo'
    compileSdkVersion '29'
    buildToolsVersion '29.0.2'
}
複製程式碼

輸出如下:

minSdkVersion = 22
applicationId = com.xfhy.gradledemo
compileSdkVersion = 29
buildToolsVersion = 29.0.2
複製程式碼

不錯,已經能搞一個能讀取到閉包資訊的外掛了.

4. 實操

我們來實現一個外掛,打包的時候將pic/test.png複製到apk檔案中的assets中.實際上是在打包之前將test.png複製到相應的資料夾,等待打包合成.首先是新建HookAssetsPlugin.groovy

class HookAssetsPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.afterEvaluate { it ->
            //1. 在afterEvaluate閉包中才能獲取當前project中的所有task
            project.plugins.withId('com.android.application') {
                //2.確保當前project是Android app project,而不是Android library project, 為給定id的外掛執行或註冊操作
                project.android.applicationVariants.all { variant ->
                    //3. 拿到所有變體資訊
                    //[ApkVariantOutputImpl_Decorated{apkData=Main{type=MAIN, fullName=freeDebug, filters=[], versionCode=2, versionName=2.0.0}}]
                    variant.outputs.each { variantOutput ->
                        // 4. outputs 欄位可知道該欄位代表著當前變體的輸出資訊(DomainObjectCollection 型別)
//                        println("${variantOutput.name}")  //free-debug  free-release
                        if (variantOutput.name.contains('release')) {
                            //5. 拿到release
                            project.tasks.findByName("package${variant.name.capitalize()}").doFirst { task ->
                                //6. 得到packageApplication
                                project.copy {
                                    //7. 複製
                                    from "${project.projectDir.absolutePath}/pic/test.png"
                                    into "${task.assets.provider.value.value.getAbsolutePath()}"
                                }

                            }

                        }
                    }
                }
            }
        }
    }
}
複製程式碼

巢狀特別多,寫起來不是很舒服,而且不能智慧提示,而且api還不好查....吐血..詳細的註釋在程式碼中已給出,這裡就不多做介紹了.使用的時候在app下的build.gradle中新增如下程式碼即可:

import com.xfhy.plugin.HookAssetsPlugin
apply plugin: HookAssetsPlugin
複製程式碼

然後執行gradlew assembleFreeRelease,完事兒之後看一下打出來的apk包,可以看到是apk檔案裡面的assets裡面確實多了test.png檔案.看起來沒什麼鳥用,但是卻讓我們感受到了外掛到底可以幹些什麼事兒.

5. 除錯Gradle

首先是我們在執行指令的時候加一個引數gradlew assembleFreeRelease -Dorg.gradle.debug=true --no-daemon,這樣. 然後在AS->Run->EditConfigurations,然後點選左上角的+號,新增一個Remote,然後直接點選apply即可.

Gradle系列(四) Gradle外掛

然後點選debug按鈕即可進入除錯.

Gradle系列(四) Gradle外掛

6. 總結

Gradle外掛很多時候能方便我們進行一些騷操作,用處還是挺大的.

參考

相關文章