欢迎关注本人公众号,扫描下方二维码或搜索公众号 id: mxszgghtml
![]()
本文基于 Android Gradle plugin 3.0.1java
在前文中笔者阐述道 task 就至关于函数,那么这篇文章所要介绍的 plugin 就至关于函数库了。毕竟在 build.gradle
文件中撰写大量的 task 是确定很差维护的,因此能够将 tasks 作成 plugin 而后直接 apply 就行了。android
就像在
app/build.gradle
中apply plugin: 'com.android.application'
这样 appProject 就可使用该 plugin 中的 task 了。git
buildSrc
。src/main/java
改为 src/main/groovy
新建一个 xxxPlugin.groovy 并实现 Plugin 接口,例如:github
import org.gradle.api.Plugin
import org.gradle.api.Project
class TestPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.task('pluginTest') {
doLast {
println 'Hello World'
}
}
}
}
复制代码
能够看到,上述 plugin 仅是在 apply()
方法内部建立了一个名为 pluginTest
的 task。api
因为 Kotlin/Java 与 groovy 的兼容,因此并不是必定要建立 groovy 文件,也能够是 xxxPlugin.java/xxxPlugin.kotlin。bash
build.gradle
文件中添加以下信息:apply plugin: TestPlugin微信
至此以后,不妨在命令行调用 pluginTest
task 看看是否有效果——闭包
./gradlew pluginTestapp
> Task :app:testPlugin
Hello from the TestPlugin
随着项目的急速发展,有朝一日发现有时候不想输出 Hello World
而是但愿这个 pluginTest
task 能够根据开发者的需求进行配置。
建立一个 xxxExtension.groovy 文件(固然,也能够用 Java/Kotlin 来写),实际上就是和 JavaBean 差很少的类,相似以下:
class TestPluginExtension {
String message = 'Hello World'
}
复制代码
在 Plugin 类中获取闭包信息,并输出:
class TestPlugin implements Plugin<Project> {
void apply(Project project) {
// Add the 'testExtension' extension object
def extension = project.extensions.create('testExtension', TestPluginExtension)
project.task('pluginTest') {
doLast {
println extension.message
}
}
}
}
复制代码
第四行经过 project.extensions.create(String name, Class<T> type, Object... constructionArguments)
来获取 testExtension
闭包中的内容并经过反射将闭包的内容转换成一个 TestPluginExtension 对象。
在 build.gradle
中添加一个 testExtension
闭包:
testExtension {
message 'Hello Gradle'
}
复制代码
在命令行键入如下信息:
./gradlew pluginTest
将会看到输出结果——
> Task :app:pluginTest
Hello Gradle
到目前为止谈及到的东西都仍是一个普通的、不能够发布到仓库的插件,若是想要将插件发布出去供他人和本身在项目中 apply,须要进行如下步骤将插件变成一个 Project——
更改 build.gradle
文件内容:
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
复制代码
此时能够观察到 External Libraries 中多出了 gradle-api/gradle-installation-beacon/groovy 库。其中,gradle 的版本是基于项目下 gradle wrapper 中配置的版本——
![]()
![]()
建立 src/main/resources/META-INF/gradle-plugins/插件名.properties
,例如 src/main/resources/META-INF/gradle-plugins/com.sample.test.properties
,而后将 properities 文件内容改成 implementation-class=Plugin 路径
,例如 implementation-class=com.sample.test.TestPlugin
。
在 build.gradle
文件中经过 apply plugin: '插件名'
引入插件 —— apply plugin: 'com.sample.test'
。
在命令行键入如下信息:
./gradlew pluginTest
将会看到输出结果——
> Task :app:pluginTest
Hello Gradle
固然,以上仅是告诉各位读者如何将 plugin 项目化,并未涉及到如何将 plugin 提交到仓库中,关于 jcenter 仓库提交方式可借鉴手摸手教你如何把项目提交到 jcenter,其余仓库提交方式读者可自行搜索。
Android 打包过程当中,一个 task 接着一个 task 的执行,每一个 task 都会执行一段特定的事情(例如第一篇文章中提到的几个 task),因此在 Gradle 插件的开发中,若是是针对打包流程的更改,实际上大部分都是 hook 某一个 task 来达到目的——例如我司的 mess 经过 hook transformClassesAndResourcesWithProguardForDebug
task (Gradle v2.0+ task)来实现对四大组件以及 View 的混淆的;美丽说的 ThinRPlugin 是经过 hook transformClassesWithDexForDebug
(Gradle v2.0+ task)来实现精简 R.class/R2.class 的。
由于 Android 现有的 task 已经很完善了,因此若是想要达到目的,只须要了解相应的 task 并在其以前或以后作一些操做便可。
为了示例而示例的简单例子实在很少,笔者只能拿起上篇文章中的示例——在 app 目录下建立 pic 文件夹,并添加一个名为 test 的 png 图片,hook apk 打包流程将该图片添加入 apk 的 assets
文件夹。
尽管这看起来真的很没有卵用。
此次为了符合实际开发要求,不妨提高必定的难度——仅在 release 包中向 assets
添加图片,而 debug 包不向 assets
中添加图片。在实际开发中有不少这样的需求,例如前文提到的 mess 是对 apk 源码进行混淆的,那么平常开发者运行的 debug 包有必要执行该 task 么?显然并不须要,应该仅在发布的时候打 release 包的时候执行该 task 就行了。
那么如何知道当前 task 是为 release 服务的呢?简单的寻找到 name 为 packageRelease
的 task 是确定不行的,平常开发中项目时常有不少种变体,例如在 app/build.gradle
中输入如下代码:
android {
...
flavorDimensions "api", "mode"
productFlavors {
demo {
dimension "mode"
}
full {
dimension "mode"
}
minApi23 {
dimension "api"
minSdkVersion '23'
}
minApi21 {
dimension "api"
minSdkVersion '21'
}
}
复制代码
此时的变种共有 3 (debug、release、androidTest) * 2(demo、full) * 2(minApi2三、minApi21)共计12种,截图以下:
那么如何为以上全部的 release 变种包的 assets
中都填入图片呢?
根据官方文档能够知道开发者能够经过 android.applicationVariants.all
获取到当前全部的 apk 变体,该变体的类型为 ApplicationVariant
,其父类 BaseVariantOutput
中含 name 字段,该字段实际上就是当前变体的名字,那么其实只须要判断该 name 字段是否包含 release 关键字便可。
建立 plugin 的基本流程已经在前文中阐述过了,直接进行核心 plugin 的撰写,HookAssetsPlugin
源码以下:
import com.android.build.gradle.api.ApkVariantOutput
import com.android.build.gradle.api.ApplicationVariant
import com.android.build.gradle.tasks.PackageApplication
import org.gradle.api.Plugin
import org.gradle.api.Project
class HookAssetsPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.afterEvaluate {
project.plugins.withId('com.android.application') {
project.android.applicationVariants.all { ApplicationVariant variant ->
variant.outputs.each { ApkVariantOutput variantOutput ->
if (variantOutput.name.equalsIgnoreCase("release")) {
variantOutput.packageApplication.doFirst { PackageApplication task ->
project.copy {
from "${project.projectDir.absolutePath}/pic/test.png"
into "${task.assets.asPath}"
}
}
}
}
}
}
}
}
}
复制代码
在第一篇文中就阐述过,只能在 project.afterEvaluate
闭包中才能获取到当前 project 中的全部 task 。
经过 project.plugins.withId('com.android.application')
确保当前 project 是 Android app project 而不是 Android library project,以此来避免无效操做,毕竟 package task 是 com.android.application
中的 task。
经过 project.android.applicationVariants.all
获取全部变体信息。
经过观察 ApplicationVariant
类的父类 BaseVariant
中 outputs 字段可知道该字段表明着当前变体的输出信息(DomainObjectCollection 类型),BaseVariantOutput
的子类 ApkVariantOutput
中的 packageApplication
即为上一篇文章中所说的 PackageAndroidArtifact
task 了。
判断当前变体是不是 release 的变体。(经过 variantOutput.name.equalsIgnoreCase("release")/variant.name.equalsIgnoreCase("release")
都是能够的。)
hook 步骤4中所说的 PackageAndroidArtifact
task,将图片复制到 assets
中。
实际上,在平常开发中寻找 task 的方式可能更多的是使用
project.tasks.findByName(name)/project.tasks.getByName(name)
,这样也更加方便,笔者在 demo 中附带了此种写法,源码戳我。
除了上面提到的 mess(mess 源码解析) 和 ThinRPlugin (笔者将会在后续的文章中对 ThinRPlugin 的源码进行解析)之外,笔者了解到的还有一些如下知名的 Gradle 插件可供读者学习:
固然,前面提到的几个 plugin 有些重量级,轻量级的笔者没有了解多少,只能推荐mess 源码解析做者的一个快速生成R2.java中fields的插件和一个快速将指定class打入maindex的插件,对新手了解 Gradle plugin 仍是很友好的。
本文实战模块的源码连接:请戳我。
笔者新建了微信群,若是读者有问题或者对笔者感兴趣,欢迎加入,因为满了100人,须要先加笔者的微信,微信备注:入群。
![]()