Gradle Builds Everything —— Extends Android Gradle Plugin

478 阅读4分钟

这个可能是作为 Android 开发想要做插件开发的时候最关心的事,我们今天看看如何去扩展 Android Gradle Plugin(以下简称 AGP)

我们扩展 AGP 的方式有两种:

  1. 利用 ApplicationVariant 和 LibraryVariant 两个类,去扩展我们的一些功能。
  2. 直接继承 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 的任务链调用中,插入一个新任务的话,用上第一种方案比较好。

准备

如果我们已经按照 上一个教程 构建好一个项目的话,我们可以直接按照这个项目开始。

增加自定义的 Java Resources

我们这一节的目的是在我们的 APK 中增加自己的任意。我们需要在 apply 的回调中写逻辑,那么这个处理起来很简单,看以下示例代码:

public class AppPlugin implements Plugin<Project> {

    @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<AbstractCopyTask> task = it.getProcessJavaResourcesProvider();
            configureProcessJavaResource(project, task);
        });
    }

    private void configureProcessJavaResource(Project project, TaskProvider<AbstractCopyTask> 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 文件:

APK

可以看见我们需要的文件已经打进去了。

那么对于其他 Task 我们依然可以如法炮制,在 Variants 迭代的过程中,我们能先拿到这些 TaskProvider,然后调用 configure 方法,在 Task 真正被创建的时候,会调用到这些方法,我们只要配置好正确的输入,它就会执行我们的输出了。

在 AGP 原有的 Task 中接入我们的 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 插件的基础用法和主线就全讲完啦。

欢迎关注我的公众号「TalkWithMobile」

公众号