Android 枚举:在打开 R8 的情况下生成 AAR 二进制文件,并在客户端应用中抛出"Function invocation 'name()'"异常



这是一个非常罕见的情况,我试过在谷歌上搜索,但不幸的是没有结果。

基本上,我想创建一个库模块,并从中构建一个aar二进制文件,供不同的客户端应用程序共享。但是,当我在库模块中创建枚举类时,发生了一件奇怪的事情:

当R8 minimyEnabled关闭时,生成的aar文件可以导入到客户端应用程序并正确编译。但当minimyEnabled处于启用状态时,它会在调用枚举时引发此错误;。name";属性:

Function invocation 'name()' expected

更多详情如下:

库内模块

标度类型kt:

enum class ScaleType constructor(private val text: String) {
LARGE("large"),
SMALL("small");
override fun toString(): String {
return text
}
}

build.gradle:

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
debug {
testCoverageEnabled = false
minifyEnabled true
proguardFiles 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
...

应用内模块:

place generated aar in libs folder

MainActivity.kt,这是调用.name时抛出错误的地方,这在kotlin中应该是完全合法的。

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.mylibrary.ScaleType
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
ScaleType.LARGE.name //compile failed
}
}

build.gradle应用程序内模块

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.myapplication"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
testCoverageEnabled = false
minifyEnabled false
shrinkResources false
proguardFiles 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
...
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.0'
implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
...
}

在深入研究了AAR二进制文件之后,我观察到当lib模块中的minimyEnabled关闭时,ScaleType类被转换为

public final enum class ScaleType private constructor(text: kotlin.String) : kotlin.Enum<com.example.mylibrary.ScaleType> {

因此,在客户端应用程序中,这是绝对好的,因为这只是kotlin调用name的方式。

ScaleType.LARGE.name //compile pass

但当minimyEnabled为true时,它被转换为

public final enum com/example/mylibrary/ScaleType extends java/lang/Enum

然后在客户端应用程序中,因为它将ScaleType视为java枚举,所以它希望开发人员调用

ScaleType.LARGE.name()
instead of 
ScaleType.LARGE.name // compile failed

我还可以确认,导入库模块作为源代码并没有这个问题,因为编译器已经将所有kotlin类统一翻译为java类。它们在这种方式上是一致的,所以没有这种kotlin到java的冲突。

这现在引起了大麻烦。对于直接导入库模块的应用程序,可以调用";。名称";。但对于使用aar二进制的应用程序,它们必须调用"。name((";,甚至更痛苦的是你仍然必须使用";。name";调试lib模块时。

如有任何帮助,我们将不胜感激。

如果你想尝试一下,你可以在这里找到示例代码

Kotlin编译器在类文件中使用Kotlin元数据来确定什么是Kotlin属性。因此,如果不维护Kotlin元数据,Kotlin编译器将无法判断name是一个属性,并将其视为Java代码,因此将坚持要求您调用name()方法。

R8在AGP 4.1测试版3中增加了对维护和重写Kotlin元数据的支持。因此,如果您更新到该版本,并将以下内容放入库收缩器配置文件中,那么有望解决您的问题。

# Keep kotlin.Metadata annotations to maintain metadata on kept items.
-keepattributes RuntimeVisibleAnnotations
-keep class kotlin.Metadata { *; }

相关内容

最新更新