何时在 Gradle 插件中应用自定义 Android 扩展?

When to apply a custom Android extension within a Gradle plugin?

我正在尝试编写一个 Gradle 插件,它封装了我项目中 android { ... } 块的通用配置设置。例如添加 dev 产品口味:

develop {
    applicationIdSuffix ".dev"
    versionCode 100000
    versionName appVersionName + " (development)"
}

在 Gradle 插件中,我按如下方式应用此配置:

class MyAndroidPlugin : Plugin<Project> {

    private lateinit var myExtension: MyExtension

    override fun apply(project: Project) {

        myExtension = project.extensions.create(MyExtension.NAME, MyExtension::class.java)

        val androidExtension = extensions.findByName("android") as AppExtension
        androidExtension.productFlavors.create("dev") {
            with(project) {
                applicationIdSuffix = ".dev"
                versionCode = 100000
                versionName = "${myExtension.appVersionName} (development)"
            }
        }
    }
}

...和我的自定义扩展 class

open class MyExtension {

    companion object {
        const val NAME = "myplugin"
    }

    var appVersionName: String? = "9.9.9"

}

当我在 Android 项目中如下配置我的插件时...

apply plugin: "com.android.application"
apply plugin: "com.example.android.gradle"

android {
    ...
}

myplugin {
    appVersionName = "3.2.1"
}

... 那么 versionName 没有被修改——它仍然是“9.9.9”。似乎“3.2.1”值应用得不够早。我试过 project.afterEvaluate { ... } 导致以下错误:

Android tasks have already been created.
This happens when calling android.applicationVariants, android.libraryVariants or android.testVariants.
Once these methods are called, it is not possible to continue configuring the model.

您遇到的问题如下:

想象一下 Gradle 从上到下遍历您的构建脚本。 它会找到您的插件并执行 apply 函数。 在该函数中,您创建 productFlavor 应用 值(versionName9.9.9 来自您的 extension ). apply 方法 returns 和 Gradle 更深入构建脚本。 它会找到您的扩展设置,将值设置为扩展,然后...继续“扫描”构建脚本。

那是它。

你需要“东西”(比如观察者)来告诉 AGP productFlavorversionName 需要更新 只要扩展的值设置.

Gradle前段时间介绍过“Lazy configuration”。 See the docs here。 这样就有可能(以 Gradle 方式)。但是 ProductFlavor.versionName 应该是 Property<String> 类型。但这不是 - 今天的状态。

无论如何。我找到了 "workaround"(我不确定这是否是解决方法或就 Gradle 的工作方式而言是否可以接受):

您可以将 ProductFlavor class 作为扩展的构造函数参数,并在扩展 属性 设置后立即更新值:

您的插件:

override fun apply(project: Project) {
    val androidExtension = project.extensions.findByName("android") as AppExtension
    val flavor = androidExtension.productFlavors.create("dev")

    val myExtension = project.extensions.create(MyExtension.NAME, MyExtension::class.java, flavor)
}

扩展名:

open class MyExtension(
    private val productFlavor: ProductFlavor
) {

    companion object {
        const val NAME = "myplugin"
    }

    var versionName: String? = "9.9.9"
        set(value) {
            productFlavor.applicationIdSuffix = ".dev"
            productFlavor.dimension = "nuts"
            productFlavor.versionCode = 100000
            productFlavor.versionName = "$value (development)"
        }
}