如何使用Android Studio開發Gradle外掛程式

來源:互聯網
上載者:User

如何使用Android Studio開發Gradle外掛程式
緣由

首先說明一下為什麼會有這篇文章。前段時間,外掛程式化以及熱修複的技術很熱,Nuwa熱修複的工具NuwaGradle,攜程動態載入技術DynamicAPK,還有希望做最輕巧的外掛程式化架構的Small。這三個App有一個共同的地方就是大量的使用了Gradle這個強大的構建工具,除了攜程的架構外,另外兩個都發布了獨立的Gradle外掛程式提供自動化構建外掛程式,或者產生熱修複的補丁。所以學習一下Gradle外掛程式的編寫還是一件十分有意義的事。

外掛程式類型

Gradle的外掛程式一般有這麼幾種:

一種是直接在項目中的gradle檔案裡編寫,這種方式的缺點是無法複用外掛程式代碼,在其他項目中還得複製一遍代碼(或者說說複製一遍檔案) 另一種是在獨立的項目裡編寫外掛程式,然後發布到中央倉庫,之後直接引用就可以了,優點就是可複用。就和上面的Nuwa和Small一樣。Gradle相關文法

本篇文章不會詳細說明Gradle相關的文法,如果要學習gradle相關的東西,請查看Gradle for Android

Gradle外掛程式開發

Gradle外掛程式是使用Groovy進行開發的,而Groovy其實是可以相容Java的。Android Studio其實除了開發Android App外,完全可以勝任開發Gradle外掛程式這一工作,下面來講講具體如何開發。

首先,建立一個Android項目。 之後,建立一個Android Module項目,類型選擇Android Library。 將建立的Module中除了build.gradle檔案外的其餘檔案全都刪除,然後刪除build.gradle檔案中的所有內容。 在建立的module中建立檔案夾src,接著在src檔案目錄下建立main檔案夾,在main目錄下建立groovy目錄,這時候groovy檔案夾會被Android識別為groovy源碼目錄。除了在main目錄下建立groovy目錄外,你還要在main目錄下建立resources目錄,同理resources目錄會被自動識別為資源檔夾。在groovy目錄下建立項目包名,就像Java包名那樣。resources目錄下建立檔案夾META-INF,META-INF檔案夾下建立gradle-plugins檔案夾。這樣,就完成了gradle 外掛程式的項目的整體搭建,之後就是小細節了。目前,項目的結構是這樣的。

開啟Module下的build.gradle檔案,輸入

apply plugin: 'groovy'apply plugin: 'maven'dependencies {    compile gradleApi()    compile localGroovy()}repositories {    mavenCentral()}

下面我們在包名下建立一個檔案,命名為PluginImpl.groovy,注意有groovy尾碼,然後在裡面輸入,注意包名替換為你自己的包名。

package cn.edu.zafu.gradleimport org.gradle.api.Pluginimport org.gradle.api.Projectpublic class PluginImpl implements Plugin {    void apply(Project project) {       project.task('testTask') << {            println "Hello gradle plugin"        }    }}

然後在resources/META-INF/gradle-plugins目錄下建立一個properties檔案,注意該檔案的命名就是你只有使用外掛程式的名字,這裡命名為plugin.test.properties,在裡面輸入

implementation-class=cn.edu.zafu.gradle.PluginImpl

注意包名需要替換為你自己的包名。

這樣就完成了最簡單的一個gradle外掛程式,裡面有一個叫testTask的Task,執行該task後會輸出一段文字,就像當初我們輸出HelloWorld一樣。

發布到本地倉庫

接著,我們需要將外掛程式發布到maven中央倉庫,我們將外掛程式發布到本地倉庫就好了,在module項目下的buidl.gradle檔案中加入發布的代碼。

repositories {    mavenCentral()}group='cn.edu.zafu.gradle.plugin'version='1.0.0'uploadArchives {    repositories {        mavenDeployer {            repository(url: uri('../repo'))        }    }}

上面的group和version的定義會被使用,作為maven庫的座標的一部分,group會被作為座標的groupId,version會被作為座標的version,而座標的artifactId組成即module名,我們讓其取一個別名moduleName。然後maven本地倉庫的目錄就是當前項目目錄下的repo目錄。
這時候,右側的gradle Toolbar就會在module下多出一個task

點擊uploadArchives這個Task,就會在項目下多出一個repo目錄,裡面存著這個gradle外掛程式。

目錄就像這樣,具體目錄結構和你的包名等一系列有關,time是我的module名。

發布到本地maven倉庫後,我們就使用它,在叫app的android項目下的gradle.build的檔案中加入

buildscript {    repositories {        maven {            url uri('../repo')        }    }    dependencies {        classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'    }}apply plugin: 'plugin.test'

apply plugin後面引號內的名字就是前文plugin.test.properties檔案的檔案名稱。而class path後面引號裡的內容,就是上面grade中定義的group,version以及moduleName所共同決定的,和maven是一樣的。

同步一下gradle,右側app下other分類下就會多出一個testTask,雙擊執行這個Task,控制台就會輸出剛才我們輸入的字串

發布到Jcenter倉庫

接下來我們將其發布到jcenter中央倉庫。參考之前寫的一篇文章 使用Android Studio將開源庫發布到Jcenter中央庫

在項目根目錄下的build.gradle檔案中加入。

dependencies {        classpath 'com.android.tools.build:gradle:2.0.0-beta6'        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'        classpath 'com.github.dcendents:android-maven-plugin:1.2'    }

在項目根路徑下建立bintray.gradle檔案,輸入

apply plugin: 'com.jfrog.bintray'apply plugin: 'maven-publish'def projectName = "timePlugin"def mavenDesc = 'your desc'def baseUrl = 'https://github.com/yourBaseUrl'def siteUrl = baseUrldef gitUrl = "${baseUrl}/yourGitUrl"def issueUrl = "${baseUrl}/yourGitIssueUrl"def licenseIds = ['Apache-2.0']def licenseNames = ['The Apache Software License, Version 2.0']def licenseUrls = ['http://www.apache.org/licenses/LICENSE-2.0.txt']def inception = '2016'def username = 'lizhangqu'install {    repositories {        mavenInstaller {            pom.project {                // Description                name projectName                description mavenDesc                url siteUrl                // Archive                groupId project.group                artifactId archivesBaseName                version project.version                // License                inceptionYear inception                licenses {                    licenseNames.eachWithIndex { ln, li ->                        license {                            name ln                            url licenseUrls[li]                        }                    }                }                developers {                    developer {                        name username                    }                }                scm {                    connection gitUrl                    developerConnection gitUrl                    url siteUrl                }            }        }    }}task sourcesJar(type: Jar) {    from sourceSets.main.allGroovy    classifier = 'sources'}task javadocJar(type: Jar, dependsOn: groovydoc) {    from groovydoc.destinationDir    classifier = 'javadoc'}artifacts {    archives javadocJar    archives sourcesJar}bintray {    user = BINTRAY_USER    key = BINTRAY_KEY    configurations = ['archives']    pkg {        repo = 'maven'        name = projectName        desc = mavenDesc        websiteUrl = siteUrl        issueTrackerUrl = issueUrl        vcsUrl = gitUrl        labels = ['gradle', 'plugin', 'time']        licenses = licenseIds        publish = true        publicDownloadNumbers = true    }}

將對應的描述性文字修改為你自己的資訊,尤其是最前面的一系列的def定義,然後在gradle.properties檔案中加入BINTRAY_USER和BINTRAY_KEY。

在你的module中apply該grade檔案

apply from: '../bintray.gradle'

右側的gradle的toolbar就會多出幾個task

之後我們先運行other下的install這個task,再執行bintrayUpload這個task,如果不出意外,就上傳了,之後不要忘記到後台add to jcenter。成功add到jcenter之後就會有link to jcenter的字樣

耐心等待add to center成功的訊息,之後就可以直接引用了,將module下的gradle檔案maven部分的定義

        maven {            url uri('../repo')        }

前面加入

    jcenter()

最終的內容如下

buildscript {    repositories {        jcenter()        maven {            url uri('../repo')        }    }    dependencies {        classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'    }}apply plugin: 'plugin.test'

就是這麼簡單,再次運行一下測試下是否成功。

最佳實務

最佳實務的來源是源自multidex,為什麼呢,因為最近當方法數超了之後,如果選擇multidex,編譯的過程就會慢很多很多,為了檢測到底是哪一步的耗時,需要編寫一個外掛程式來統計各個task執行的時間,因此就有了這麼一個最佳實務。

在PluginImpl同級目錄下建立TimeListener.groovy檔案。輸入

package cn.edu.zafu.gradleimport org.gradle.BuildListenerimport org.gradle.BuildResultimport org.gradle.api.Taskimport org.gradle.api.execution.TaskExecutionListenerimport org.gradle.api.initialization.Settingsimport org.gradle.api.invocation.Gradleimport org.gradle.api.tasks.TaskStateimport org.gradle.util.Clockclass TimeListener implements TaskExecutionListener, BuildListener {    private Clock clock    private times = []    @Override    void beforeExecute(Task task) {        clock = new org.gradle.util.Clock()    }    @Override    void afterExecute(Task task, TaskState taskState) {        def ms = clock.timeInMs        times.add([ms, task.path])        task.project.logger.warn "${task.path} spend ${ms}ms"    }    @Override    void buildFinished(BuildResult result) {        println "Task spend time:"        for (time in times) {            if (time[0] >= 50) {                printf "%7sms  %s\n", time            }        }    }    @Override    void buildStarted(Gradle gradle) {}    @Override    void projectsEvaluated(Gradle gradle) {}    @Override    void projectsLoaded(Gradle gradle) {}    @Override    void settingsEvaluated(Settings settings) {}}

然後將PluginImpl檔案中的apply方法修改為

void apply(Project project) {       project.gradle.addListener(new TimeListener())    }

完成後打包發布到jcenter()。之後你只要引用了該外掛程式,就會統計各個task執行的時間,比如運行app,就會輸出像下面的資訊。

最佳實務的末尾,推廣一下這個外掛程式,這個外掛程式我已經將其發布到jcenter倉庫,如果要使用的話加入下面的代碼即可

buildscript {    repositories {        jcenter()    }    dependencies {        classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'    }}apply plugin: 'plugin.time'
傳遞參數

上面的是小試牛刀了下,接下來我們需要獲得自訂的參數。

首先按照上面的步驟建立一個module。建立PluginExtension.groovy,輸入

public class PluginExtension {    def param1 = "param1 defaut"    def param2 = "param2 defaut"    def param3 = "param3 defaut"}

然後我們希望能傳入嵌套的參數,再建立一個PluginNestExtension.groovy,輸入

public class PluginNestExtension {    def nestParam1 = "nestParam1 defaut"    def nestParam2 = "nestParam2 defaut"    def nestParam3 = "nestParam3 defaut"}

然後建立一個CustomTask.groovy,繼承DefaultTask類,使用 @TaskAction註解標註實現的方法

public class CustomTask extends DefaultTask {    @TaskAction    void output() {        println "param1 is ${project.pluginExt.param1}"        println "param2 is ${project.pluginExt.param2}"        println "param3 is ${project.pluginExt.param3}"        println "nestparam1 is ${project.pluginExt.nestExt.nestParam1}"        println "nestparam2 is ${project.pluginExt.nestExt.nestParam2}"        println "nestparam3 is ${project.pluginExt.nestExt.nestParam3}"    }}

只是做了拿到了參數,然後做最簡單的輸出操作,使用 ${project.pluginExt.param1}${project.pluginExt.nestExt.nestParam1}等拿到外部的參數。

別忘了在META-INF/gradle-plugins目錄下建立properties檔案指定外掛程式的介面實作類別。

複製之前建立的PluginImpl.groovy到包下,修改apply方法

public class PluginImpl implements Plugin {    void apply(Project project) {        project.extensions.create('pluginExt', PluginExtension)        project.pluginExt.extensions.create('nestExt', PluginNestExtension)        project.task('customTask', type: CustomTask)    }}

將外掛程式發布到本地maven後,進行引用。

buildscript {    repositories {        maven {            url uri('../repo')        }    }    dependencies {        classpath 'cn.edu.zafu.gradle.plugin:test:1.0.0'    }}apply plugin: 'plugin.test'

定義外部參數,這裡我們定義了param1,param2,nestParam1,nestParam2,此外param3和nestParam3保持預設。

pluginExt {    param1 = 'app param1'    param2 = 'app param2'    nestExt{        nestParam1='app nestParam1'        nestParam2='app nestParam2'    }}

同步一下gradle,執行customTask。

上面的代碼很簡單,不用解釋也能看到,所以不再解釋了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.