写 Gradle 插件的一点经验

来源:互联网 发布:一骑当千动作数据 编辑:程序博客网 时间:2024/05/01 18:01

本着简单易用的原则,参考 android-resource-remover 写了一个删除无用资源文件的 Gradle 插件 - clean-unused-resources-gradle-plugin,结果微博发出来不到10分钟,陈启超就告诉我 AS2.0+ 已经提供了此功能。天哪,为了纪念这个短命无用的轮子,只好写篇博客,把造轮子的过程记录下来,也算对别人有点用处。

官方文档说了,自定义 Gradle 插件有三种方式:

  1. Build script
  2. buildSrc project
  3. Standalone project

但是,AS 不完美支持第三种方式,我们用 AS 的爸爸 IntelliJ IDEA CE 就好了。

IntelliJ IDEA CE

首先 New 一个基于 Gradle 的 Groovy 工程:

修改一下自动生成的 build.gradle 文件,把 repositoriesdependencies 替换掉,其他保持不变。

repositories {  jcenter()}dependencies {  compile gradleApi()  testCompile group: 'junit', name: 'junit', version: '4.11'}

然后创建 srcresources 目录(以 clean-unused-resources-gradle-plugin 为例):

project structure

resources/META-INF/gradle-plusings/ 是必不可少,否则别人无法使用你的插件,目录下的 *.properties 文件名就是插件的名字,别人apply 的时候会用到:

apply plugin: 'com.youzan.mobile.cleaner'

文件内容是把 implementation-class 指向你插件类的全名。

implementation-class=com.youzan.mobile.CleanerPlugin

CleanerPlugin.groovy 实现了接口Plugin<Project>,而 org.gradle.api.Plugin 就是由 compile gradleApi() 提供,我们在 build.gradle 的 dependencies 中已经添加过了。

准备就绪,开始写插件。

首先,实现 apply 方法:

class CleanerPlugin implements Plugin<Project> {  @Override  void apply(Project project) {    // 创建一个 extension    project.extensions.create('resourceCleaner', CleanerExtension);    // 修改 lint report 路径    project.afterEvaluate {      project.android.lintOptions.xmlOutput = new File(project.buildDir, "lintResult.xml");    }    // 创建 task    project.tasks.create('cleanResource', CleanTask)  }}

第一步通过 project.extensions.create 创建一个 extension

CleanerExtension.groovy

class CleanerExtension {  Iterable<String> excludedFiles}

这个 extension 用于别人向你的插件传递参数,例如:

resourceCleaner {   excludedFiles = [     'string_pos.xml',     'string_car.xml',   ]}

第二步修改 lint report 的路径:

project.afterEvaluate {  project.android.lintOptions.xmlOutput = new File(project.buildDir, "lintResult.xml");}

这里用到了 Android Gradle Plugin 的 DSL,所以 IDEA 无法动态提示,没关系,我们直接去翻文档,里面有详解的解释:

Android Plugin DSL References

配置参数(CleanerExtension)和文件参数(lintOptions.xmlOutput)都准备好了。

第三步,主角上场,创建一个 task

class CleanTask extends DefaultTask {  CleanTask() {    super()    dependsOn "lint"  }  @TaskAction  def clean() {    def lintResult = project.android.lintOptions.xmlOutput    def excludedFiles = project.resourceCleaner.excludedFiles    Cleaner.clean(lintResult, excludedFiles)  }}

CleanTask 继承自 DefaultTask,因为 CleanTask 的输入是 lint report,所以在构造方法中通过调用 dependsOn "lint" 让自己依赖于 lint 这个 task

CleanTask 就好像动物农场里面的猪,只负责发号施令,安排工作,真正干活的“人”是 Cleaner

class Cleaner {    def static clean(File report, Iterable<String> excludedFiles) {        def issues = new XmlSlurper().parse(report)        issues.'*'.findAll {            it.name() == 'issue' && it.@id == 'UnusedResources'        }.each {            def file = new File(it.location.@file.text())            if (file.name in excludedFiles) return;            def line = it.location.@line            def column = it.location.@column            if ((line == '' && column == '') || column == '1') {                println "deleting " + file.path                file.delete()            } else {                def m = it.@message =~ $/`R.(\\w+).([^`]+)`/$                if (!m) return;                def type = m.group(1)                def entryName = m.group(2);                def parsed = new XmlSlurper().parse(file)                parsed.'**'.findAll {                    it.@name == entryName && it.name().contains(type)                }*.replaceNode {}                XmlUtil.serialize(parsed, new FileWriter(file))            }        }    }}

一个简单的独立工程的 Gradle Plugin 就这么写完了,是不是非常简单,先不要高兴,还有最后一步 - 发布到 jCenter。

继续修改 build.gralde

添加 bintray 插件。

plugins {    id "com.jfrog.bintray" version "1.4"}Properties properties = new Properties();properties.load(project.rootProject.file('local.properties').newDataInputStream())bintray {    user = properties.getProperty("bintray.user")    key = properties.getProperty("bintray.apikey")    publications = ['mavenJava']    pkg {        repo = 'maven'        name = 'cleaner-gradle-plugin'        desc = 'a gradle plugin to clean unused resources detected by Lint'        websiteUrl = 'https://github.com/YouzanMobile/clean-resource-gradle-plugin'        issueTrackerUrl = 'https://github.com/YouzanMobile/clean-resource-gradle-plugin/issues'        vcsUrl ='https://github.com/YouzanMobile/clean-resource-gradle-plugin'        publicDownloadNumbers = true        licenses = ['MIT']    }}

maven-publish 插件:

apply plugin: 'maven-publish'// custom tasks for creating source/javadoc jarstask sourcesJar(type: Jar, dependsOn: classes) {    classifier = 'sources'    from sourceSets.main.allSource}task javadocJar(type: Jar, dependsOn: javadoc) {    classifier = 'javadoc'    from javadoc.destinationDir}// add javadoc/source jar tasks as artifactsartifacts {    archives sourcesJar, javadocJar}publishing {    publications {        mavenJava(MavenPublication) {            from components.java            artifact sourcesJar            artifact javadocJar            groupId 'com.youzan.mobile'            artifactId 'cleaner-gradle-plugin'            version versionName        }    }}

OK,大功告成,演出结束,下面是致谢:

  • NuwaGradle
  • 陈启超_V
  • Gradle User Guide
1 0