}
}
}
第四行通过 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(minApi23、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 {
@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 “
p
r
o
j
e
c
t
.
p
r
o
j
e
c
t
D
i
r
.
a
b
s
o
l
u
t
e
P
a
t
h
/
p
i
c
/
t
e
s
t
.
p
n
g
"
i
n
t
o
"
{project.projectDir.absolutePath}/pic/test.png" into "
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 插件可供读者学习:
- tinker-patch-gradle-plugin( Android 热修复 Tinker Gradle Plugin解析)
- butterknife-gradle-plugin
- build-time-tracker-plugin(检测使用 Gradle 构建项目时 每一个 Task 的耗时 )
- gradle-small-plugin(轻量级插件化框架——Small)
当然,前面提到的几个 plugin 有些重量级,轻量级的笔者没有了解多少,只能推荐mess 源码解析作者的一个快速生成R2.java中fields的插件和一个快速将指定class打入maindex的插件,对新手了解 Gradle plugin 还是很友好的。
本文实战模块的源码链接:请戳我。
笔者新建了微信群,如果读者有问题或者对笔者感兴趣,欢迎加入,由于满了100人,需要先加笔者的微信,微信备注:入群。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/98d3bee3400b82e116622c41b8a609fe.jpeg)
面试复习笔记:
这份资料我从春招开始,就会将各博客、论坛。网站上等优质的Android开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
《960页Android开发笔记》
《1307页Android开发面试宝典》
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!