How to access variant.outputFileName in Kotlin

ⅰ亾dé卋堺 提交于 2020-01-11 02:05:07

问题


We've been using a snippet like this one to rename the APK file generated by our Gradle build:

android.applicationVariants.all { variant ->
    variant.outputs.all {
        outputFileName = "${variant.name}-${variant.versionName}.apk"
    }
}

Source: https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration#variant_output

I am now in the process of converting my build.gradle to build.gradle.kts, i. e. to the Gradle Kotlin DSL. This is one of the last missing pieces: I can't figure out how to access outputFileName.

According to the API docs it does not even seem to exist:

  • BaseVariant.getOutputs() returns a DomainObjectCollection<BaseVariantOutput> which provides the all method used in the snippet.
  • BaseVariantOutput extends OutputFile which extends VariantOutput but none of these has an outputFileName or any getters or setters of a matching name.

So, I suspect there is some advanced Groovy magic at work to make this work - but how do I get there in Kotlin?


回答1:


Browsing through the source code of the Android Gradle plugin, I think I found the answer - here we go:

We are actually dealing with objects of type BaseVariantOutputImpl and this class does have both these methods:

public String getOutputFileName() {
    return apkData.getOutputFileName();
}

public void setOutputFileName(String outputFileName) {
    if (new File(outputFileName).isAbsolute()) {
        throw new GradleException("Absolute path are not supported when setting " +
                    "an output file name");
    }
    apkData.setOutputFileName(outputFileName);
}

Using this knowledge we can now:

import com.android.build.gradle.internal.api.BaseVariantOutputImpl

and then cast our target objects like so:

applicationVariants.all(object : Action<ApplicationVariant> {
    override fun execute(variant: ApplicationVariant) {
        println("variant: ${variant}")
        variant.outputs.all(object : Action<BaseVariantOutput> {
            override fun execute(output: BaseVariantOutput) {

                val outputImpl = output as BaseVariantOutputImpl
                val fileName = output.outputFileName
                        .replace("-release", "-release-v${defaultConfig.versionName}-vc${defaultConfig.versionCode}-$gitHash")
                        .replace("-debug", "-debug-v${defaultConfig.versionName}-vc${defaultConfig.versionCode}-$gitHash")
                println("output file name: ${fileName}")
                outputImpl.outputFileName = fileName
            }
        })
    }
})

So, I guess: Yes, there is some Groovy magic at work, namely that Groovy's dynamic type system allows you to just access getOutputFileName and setOutputFileName (by way of the abbreviated outputImpl.outputFileName syntax, as in Kotlin) from your code, hoping they will be there at runtime, even if the compile time interfaces that you know about don't have them.




回答2:


A little simplified version of @david.mihola answer:

android {

    applicationVariants.forEach { variant ->
        variant.outputs
                .map { it as BaseVariantOutputImpl }
                .forEach { output ->
                    output.outputFileName = output.outputFileName
                            .replace("app-", "FooBar-")
                            .replace(".apk", "-${variant.versionName}.${variant.versionCode}.apk")
                }
    }

}


来源:https://stackoverflow.com/questions/50792428/how-to-access-variant-outputfilename-in-kotlin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!