首页 > 其他分享 >写 Gradle 插件的一点经验

写 Gradle 插件的一点经验

时间:2023-01-04 14:37:04浏览次数:62  
标签:插件 经验 plugin gradle project clean Gradle def


写 Gradle 插件的一点经验_gradle

本着简单易用的原则,参考 ​​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​​ 就好了。

写 Gradle 插件的一点经验_javad_02

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

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

repositories {
jcenter()
}

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

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

写 Gradle 插件的一点经验_xml_03

​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 无法动态提示,没关系,我们直接去翻​​文档​​,里面有详解的解释:

写 Gradle 插件的一点经验_Gradle_04

配置参数(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([email protected]())
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 jars
task 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 artifacts
artifacts {
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,大功告成,演出结束,下面是致谢:


标签:插件,经验,plugin,gradle,project,clean,Gradle,def
From: https://blog.51cto.com/u_15929756/5988605

相关文章