如何在 Kotlin 中访问 variant.outputFileName
Posted
技术标签:
【中文标题】如何在 Kotlin 中访问 variant.outputFileName【英文标题】:How to access variant.outputFileName in Kotlin 【发布时间】:2018-11-20 09:23:11 【问题描述】:我们一直在使用类似这样的 sn-p 来重命名 Gradle 构建生成的 APK 文件:
android.applicationVariants.all variant ->
variant.outputs.all
outputFileName = "$variant.name-$variant.versionName.apk"
来源:https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration#variant_output
我现在正在将我的build.gradle
转换为build.gradle.kts
,即。 e.到 Gradle Kotlin DSL。这是最后丢失的部分之一:我不知道如何访问outputFileName
。
根据 API 文档,它甚至似乎都不存在:
BaseVariant.getOutputs()
返回一个 DomainObjectCollection<BaseVariantOutput>
,它提供了在 sn-p 中使用的 all
方法。
BaseVariantOutput
扩展 OutputFile
扩展 VariantOutput
但这些都没有 outputFileName
或任何匹配名称的 getter 或 setter。
所以,我怀疑有一些高级的 Groovy 魔法在起作用,但我如何在 Kotlin 中做到这一点?
【问题讨论】:
致反对者:想解释一下吗? 【参考方案1】:@david.mihola 答案的一点简化版本:
android
/**
* Notes Impl: Use DomainObjectCollection#all
*/
applicationVariants.all
val variant = this
variant.outputs
.map it as com.android.build.gradle.internal.api.BaseVariantOutputImpl
.forEach output ->
val outputFileName = "YourAppName - $variant.baseName - $variant.versionName $variant.versionCode.apk"
println("OutputFileName: $outputFileName")
output.outputFileName = outputFileName
【讨论】:
能否请您解释一下这个合成器/样本以及为什么我们仍然需要使用 BaseVariantOutputImpl ? 因为我们需要转换它,所以我们可以访问 setOuputFileName @Zingam cmiiw @mochdawi 那么这是由于 Kotlin DSL 的一些严重缺陷造成的吗? 根据我的经验,applicationVariants.forEach 不起作用(集合为空),您必须使用 applicationVariants.all。但不是 Kotlin 的扩展 Iterable.all(),而是 DomainObjectCollection.all()。【参考方案2】:浏览了Android Gradle插件的源码,我想我找到了答案——开始吧:
我们实际上是在处理BaseVariantOutputImpl
类型的对象,而这个类确实有这两种方法:
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);
利用这些知识,我们现在可以:
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
然后像这样投射我们的目标对象:
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
)
)
所以,我猜:是的,有一些 Groovy 魔法在起作用,即 Groovy 的动态类型系统允许您只访问 getOutputFileName
和 setOutputFileName
(通过缩写 outputImpl.outputFileName
语法,如 Kotlin ) 从您的代码中提取,希望它们在运行时存在,即使您知道的编译时接口没有它们。
【讨论】:
非常感谢您提供详细的解释+解决方案:) @david.mihola 注意:您可以调用更简单的 applicationVariants.all(无需创建显式 Action 对象),您的 ApplicationVariant 将作为接收器参数传递。确保不要定义一些 lambda 参数:“.all variant->”,否则它将匹配 Kotlin 的扩展函数。只使用简单的“.all”,你会得到更短更好的代码。【参考方案3】:使用 lambdas 的较短版本:
applicationVariants.all
outputs.all
if(name.contains("release"))
(this as BaseVariantOutputImpl).outputFileName = "../../apk/$name-$versionName.apk"
这会将 APK 放入 app/apk 文件夹,名称由变体名称和版本代码组成。 您可以根据需要更改文件名的格式。 重要提示:必须仅在发布版本上执行,因为路径中的“..”会破坏调试构建过程并出现奇怪的错误。
【讨论】:
以上是关于如何在 Kotlin 中访问 variant.outputFileName的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Kotlin 中访问 variant.outputFileName