这个可能是作为 Android 开发想要做插件开发的时候最关心的事,我们今天看看如何去扩展 Android Gradle Plugin(以下简称 AGP)
我们扩展 AGP 的方式有两种:
利用 ApplicationVariant 和 LibraryVariant 两个类,去扩展我们的一些功能。
直接继承 AppPlugin 和 LibraryPlugin 来实现扩展功能。
其中,第一种方式是我们常见的方式,我们经常会使用 Gradle 的 DSL 获取到所有的变体。
applicationVariants.all { variant ->
... // Do something
}
applicationVariants 是 AppExtension 里的内容,如果你想配置 Library 工程的话,就使用 libraryVariants
,这两个 DSL 调用返回的是两种对象的集合,一个是 ApplicationVariant 这个类的集合,一个是 LibraryVariant 的集合,他们都继承于 BaseVariant,我们看下两个对象的简单声明:
public interface ApplicationVariant extends ApkVariant, TestedVariant {}
public interface LibraryVariant extends BaseVariant, TestedVariant {
...
}
他们统一继承于 BaseVariant(ApkVariant 继承自 BaseVariant),因此可以使用 BaseVariant 获得一些接口。首先,我们目前使用的版本是 AGP 3.5.x,BaseVariant 提供了一些主要的 TaskProvider,我们能拿到相关 Variant 的 TaskProvider 做一些配置,比如我们关心 Java 编译任务的话,可以使用 getJavaCompileProvider
这个方法,拿到 TaskProvider对象,然后使用 .config
方法对这个 Task 进行调用。一般来说,使用 BaseVariant 的方式能满足基本需求,
第二种方式的自定义能力更强大,同时接入会更加复杂,我们继承了 AppPlugin 就意味着需要子类化 TaskManager 等类,这时候,如果我们想子类化一些 AGP 提供的任务的话,用这种方式会好很多,如果我们只想在 AGP 的任务链调用中,插入一个新任务的话,用上第一种方案比较好。
如果我们已经按照 上一个教程 构建好一个项目的话,我们可以直接按照这个项目开始。
我们这一节的目的是在我们的 APK 中增加自己的任意。我们需要在 apply
的回调中写逻辑,那么这个处理起来很简单,看以下示例代码:
public class AppPlugin implements Plugin {
@Override
public void apply(Project project) {
project.afterEvaluate(p -> {
AppExtension extension = (AppExtension)p.getExtensions().getByName("android");
Set extends BaseVariant> variants = extension.getApplicationVariants();
configureVariants(project, variants);
});
}
private void configureVariants(Project project, Set extends BaseVariant> variants) {
variants.forEach(it -> {
TaskProvider task = it.getProcessJavaResourcesProvider();
configureProcessJavaResource(project, task);
});
}
private void configureProcessJavaResource(Project project, TaskProvider taskProvider) {
taskProvider.configure(task -> {
task.from(project.file("gemini.txt"));
});
}
}
AbstractCopyTask.from 的注释如下:
/**
* Specifies source files or directories for a copy. The given paths are evaluated as per {@link
* org.gradle.api.Project#files(Object...)}.
*
* @param sourcePaths Paths to source files for the copy
*/
CopySourceSpec from(Object... sourcePaths);
那么经过以上修改,我们就成功的往 JavaResources 任务里面新增了一个 Input 啦。赶紧使用
./gradlew clean install
把插件安装到本地,然后在 Android 工程中进行实践吧。打开我们上次创建的 Android 工程,然后在 app
模块中新建刚刚需要的文件,比如我这里使用的是 gemini.txt
最后,执行 Android 工程的任务:
./gradlew clean assembleDebug
完成后,我们查看下我们的 APK 文件:
可以看见我们需要的文件已经打进去了。
那么对于其他 Task 我们依然可以如法炮制,在 Variants 迭代的过程中,我们能先拿到这些 TaskProvider,然后调用 configure 方法,在 Task 真正被创建的时候,会调用到这些方法,我们只要配置好正确的输入,它就会执行我们的输出了。
我们在前面 Gradle Builds Everything —— Task 实例 说过,Task 之间的产物,可以使用 BuildableArtifactsHolder 这个对象连接起来。因为它是一个产物收集器,在最后 Package 任务需要打包 zip 的过程中,都是通过这个类把所有已经打出的产物收集起来,最后变成一个 zip 包。
task.outputFile = variantScope.getArtifacts().createArtifactFile(
InternalArtifactType.BUNDLE,
BuildArtifactsHolder.OperationType.INITIAL,
taskName,
bundleName)
我们目前只能通过继承的方式拿到 VariantScope,同时只能通过子类化 TaskManager 的方式重新编排 Configuration 过程中的顺序(任务执行顺序我们反而不用担心)。通过精心编排好 BuildableArtifactsHolder 的注册顺序,Task 就自动被串联起来了。
后续可能还有关于 Task 输入和输出的高级用法,比如像前文提到的 Artifacts 之类的连接等等。不过到此为止,我们关于自定义 Gradle 插件的基础用法和主线就全讲完啦。