Gradle插件开发学习和实践

本文链接

https://www.jianshu.com/p/32c150f0cb20

gradle.jpg

本文环境基础

  • Gradle:4.1

  • AndroidGradleTools:3.0.1

  • AndroidStudio:3.0.1

Talk is cheap. Show me the code.

https://github.com/sandy1108/MyTestGradlePluginProject

工程配置

  1. 在AndroidStudio中,新建一个Android工程。

  2. 新工程中, 可以再创建一个Android的Module。核心内容如下:

-project
    -libs
    -src
        -androidTest
        -main
            -java
            -res
            -AndroidManifest.xml
        -test
    -build.gradle
  1. 我们改为这样:
-project
    -libs
    -src
        -main
            -java
            -groovy
                -org
                    -wsgh
                        MyGradlePlugin.groovy
            -resources
                -META-INF
                    -gradle-plugins
                        mygradleplugin.properties
    -build.gradle

其中,mygradleplugin.properties的文件名就是你的gradle插件名成,也就是最后你在gradle中apply plugin指定的那个插件名。而此文件内的内容如下:

implementation-class = org.wsgh.MyGradlePlugin

这里面指定的就是你的Gradle插件的入口类。然后你就可以在groovy目录中对应的按照包路径新建一个以此命名的groovy文件,作为入口类。

  1. build.gradle改为:
apply plugin: 'groovy'

dependencies {
    compile gradleApi()
    compile localGroovy()
}
  1. MyGradlePlugin.groovy中:
public class MyGradlePlugin implements Plugin<Project>{
    @Override
    void apply(Project project) {
        //TODO gradle中apply这个插件时,会执行这个回调方法,我们可以在这里处理一些逻辑,进行插件的初始化操作。
        println("Hello Groovy World!!!")
    }
}
  1. 如果为了调试方便,可以修改步骤2中新建的module的名字,为buildSrc。这样,在整个project的gradle文件中,就可以直接apply你这个plugin,查看效果了。至此,基本工程配置就算完成了。

Gradle插件基本内容:Task

Task理解

一个Task包含若干Action。所以,Task有doFirst和doLast等函数,用于添加需要最先执行的Action和需要和需要最后执行的Action。Action就是一个闭包。闭包,英文叫Closure,是Groovy中非常重要的一个数据类型或者说一种概念。

Task创建

  1. 普通方式
task myTask
task myTask { configure closure }  // closure是一个闭包,代表任务内容

  1. “继承”方式

Type可以指定任务类型,实际上就类似于继承的关系,创建了一个任务的子类任务。Gradle本身提供有Copy、Delete、Sync等,你也可以继承你自己定义的Task。

task myTask(type: SomeType)
task myTask(type: SomeType) { configure closure }
  1. 过时方式(据说Gradle5.0就要删了)

<<符号是doLast的缩写,现在已经过时,推荐直接用doLast

task myType << { task action }
  1. Task“动态”创建

其实这里说动态创建,只是我自己这么称呼的,因为这是可以用字符串作为task名称的方法,是一种看起来更灵活一点的创建方式。

//project为当前工程的project对象,在gradle脚本中可以直接用,若是groovy的gradle插件的话,在实现apply方法时,会回调来一个project对象。

project.tasks.create("YourTaskName")

Task执行顺序变换

  1. doLast和doFirst:将Action追加到Task最后面(最前面)。

代码举例:

        def task = project.tasks.create("task")
        task.doLast {
            println("===============>task, Last1")
        }
        task.doLast {
            println("===============>task, Last2")
        }
        task.doFirst {
            println("===============>task, First1")
        }
        task.doFirst {
            println("===============>task, First2")
        }

输出结果应该是:

===============>task, First2
===============>task, First1
===============>task, Last1
===============>task, Last2
  1. dependsOn:声明本Task在执行之前必须先执行完成dependsOn后面指定的Task,可以指定多个Task为依赖,顺序不明,据说貌似是按照Task名称排序,但这不重要。

代码举例:

        def task2 = project.tasks.create("task2")
        task2.doLast {
            println("===============>task2")
        }
        def task1 = project.tasks.create("task1")
        task1.doLast {
            println("===============>task1")
        }
        task1.dependsOn(task2)

这时,我们直接执行task1,无需执行task2。输出结果应该是:

===============>task2
===============>task1
  1. finalizedBy:声明本Task在执行之后必须要继续执行完成finalizedBy后面指定的Task,可以指定多个Task为结尾,顺序同上。

代码举例:

        def task2 = project.tasks.create("task2")
        task2.doLast {
            println("===============>task2")
        }
        def task1 = project.tasks.create("task1")
        task1.doLast {
            println("===============>task1")
        }
        task1.finalizedBy(task2)

这时,我们直接执行task1,无需执行task2。输出结果应该是:

===============>task1
===============>task2
  1. mustRunAfter:为啥dependsOn和finalizedBy不能规定顺序呢?没事,这个mustRunAfter就可以解决。可以规定当多个无序任务在执行时,谁必须在谁后面执行。

代码举例:

        def task2 = project.tasks.create("task2")
        task2.doLast {
            println("===============>task2")
        }
        def task1 = project.tasks.create("task1")
        task1.doLast {
            println("===============>task1")
        }
        def task3 = project.tasks.create("task3")
        task3.doLast {
            println("===============>task3")
        }
        def task = project.tasks.create("testMain")
        task.doLast {
            println("===============>taskMainLastDo")
        }
        task.doFirst {
            println("===============>taskMainFirstDo")
        }
        task.dependsOn(task1,task2,task3)
        task2.mustRunAfter(task3)

直接执行task任务,无需执行task1,task2,task3。输出结果应该为:

:task1
===============>task1
:task3
===============>task3
:task2
===============>task2
:testMain
===============>taskMainFirstDo
===============>taskMainLastDo
  1. 以上这几种排序的方式基本解决了我的一些常用需求,如果还有更好的方式,欢迎大神指点!

发布在本地Maven

apply plugin: 'maven'

group='org.wsgh.gradle.plugins'
version='1.0.0'
def artifactName='my-gradle-plugin'

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: uri('/Users/zhangyipeng/Maven')){
                //自定义maven仓库最后的artifactId,也就是版本号前面的那部分名字。不定义的话,默认会采用module名
                pom.artifactId = artifactName
            }
        }
    }
}

从Maven引用

buildscript {
    repositories {
        maven {
            url uri('/Users/zhangyipeng/Maven')
        }
    }
    dependencies {
        classpath "org.wsgh.gradle.plugins:my-gradle-plugin:1.0.0"
    }
}

执行和测试Task

  1. AndroidStudio右边有个Gradle标签,点击会展开一个列表,展开你已经apply了plugin的那个module(或者我是在root project测试的),去other中找,就看到你定义的task了,双击即可执行。

  2. 另一种方法,点开AndroidStudio下面的Terminal,应该是直接在当前工程目录的,直接执行。比如我要执行testMain任务:

    ./gradlew testMain
    
    //如果是Mac系统,可能会报没有权限的错误,可以执行下面代码加权限
    chmod +x gradlew
    

应用场景举例(待完善)

想整理一些例子,不过感觉意义不是特别大。今后慢慢完善吧。

版本号自增长

工作中需要自动化发布一个SDK包,现在最后想要每次递增版本号,如何实现呢?想了一个思路,就是写入本地配置文件,每次使用记录一下。这里用到了Properties类进行读写配置文件。在此记录:

    def getEngineLocalBuildVersionCode(String engineVersion) {
        def propertiesFileName = 'local-appcanengine-build-versions.properties'
        def versionPropertiesFile = new File(propertiesFileName)
        if (!versionPropertiesFile.exists()){
            println("Local BuildVersion Record file is not found,create it:${propertiesFileName}")
            versionPropertiesFile.createNewFile();
        }
        if (versionPropertiesFile.canRead()) {
            def Properties versionProps = new Properties()
            versionPropertiesFile.withInputStream {
                fileStream -> versionProps.load(fileStream)
            }
            def versionCode = 0;
            def buildVersionCodeStr = versionProps[engineVersion]
            if (buildVersionCodeStr != null) {
                versionCode = buildVersionCodeStr.toInteger()
            }
            versionProps[engineVersion] = (++versionCode).toString()
            versionProps.store(versionPropertiesFile.newWriter(), "Output AppCanEngine Increasement Local BuildVersion Record. \nIt is an AUTO-GENERATE file in AppCanEngine's compiling. Please Do NOT modify it manually.")
            if (versionCode < 10){
                buildVersionCodeStr = "0${versionCode}"
            }else{
                buildVersionCodeStr = versionCode;
            }
            println("AppCanEngine buildVersion is ${buildVersionCodeStr}")
            return buildVersionCodeStr
        } else {
            throw GradleException("Cannot find or create ${propertiesFileName}!")
        }
    }

新版本兼容问题:GradleWrapper配置为4.1版本以上,AndroidGradleTools配置为3.0.1以上

  1. BaseVariantOutputData类没了,直接用父类就行了:
import com.android.build.gradle.internal.variant.BaseVariantOutputData
  1. sdkHandler属性没了,之前通过这个属性来获取sdk目录ndk目录等信息,现在要更换实现方式了:
project.plugins.findPlugin(‘com.android.application’).sdkHandler

应更换其他的实现方式。举例如下:

project.android.sdkDirectory

或者:

def plugin = project.plugins.findPlugin('android') ?:
                project.plugins.findPlugin('android-library')

plugin.extension.sdkDirectory
  1. repositories中,增加google(),否则一些新版的库在jcenter中找不到了。

学习过程参考

Gradle for Android

Gradle Task的使用

如何使用Android Studio开发Gradle插件

官方API

使用Groovy操作文件

Gradle User Guide 中文版

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容