java - 安卓枚举 : generate AAR binary with R8 on and throws "Function invocation ' name( )'" exception in client app

标签 java android kotlin enums android-r8

这是一个非常罕见的案例,我已经尝试过用谷歌搜索,但不幸的是没有任何结果。
基本上我想创建一个库模块并从中构建一个 aar 二进制文件以供不同的客户端应用程序共享。但是当我在库模块中创建一个枚举类时,发生了一件奇怪的事情:
当 R8 minifyEnabled 关闭时,生成的 aar 文件可以导入到客户端应用程序并正确编译。但是当 minifyEnabled 打开时,它开始抛出这个错误来调用枚举“.name”属性:

Function invocation 'name()' expected
更多详情如下:
在库模块中
ScaleTyp.kt:
enum class ScaleType constructor(private val text: String) {
    LARGE("large"),
    SMALL("small");
    override fun toString(): String {
        return text
    }
}
构建.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 二进制文件后,我观察到当 minifyEnabled 在 lib 模块中关闭时,ScaleType 类被转换为
public final enum class ScaleType private constructor(text: kotlin.String) : kotlin.Enum<com.example.mylibrary.ScaleType> {
因此在客户端应用程序中这绝对没问题,因为这只是 kotlin 调用名称的方式。
ScaleType.LARGE.name //compile pass
但是当 minifyEnabled 为真时,它被翻译为
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 的冲突。
这现在造成了很大的麻烦。对于直接导入库模块的应用,调用“.name”即可。但是对于使用 aar 二进制的应用程序,它们必须调用“.name()”,更痛苦的是,在调试 lib 模块时仍然必须使用“.name”。
任何帮助,将不胜感激。
如果你想尝试一下,你可以找到示例代码 here

最佳答案

Kotlin 编译器使用类文件中的 Kotlin 元数据来确定什么是 Kotlin 属性。因此,如果不维护 Kotlin 元数据,Kotlin 编译器将无法判断 name是一个属性,它会将其视为 Java 代码,因此会坚持您调用 name()方法。
R8 在 AGP 4.1 beta3 中增加了对维护和重写 Kotlin 元数据的支持。因此,如果您更新到该版本并将以下内容放入您的库压缩器配置文件中,希望这应该可以解决您的问题。

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

关于java - 安卓枚举 : generate AAR binary with R8 on and throws "Function invocation ' name( )'" exception in client app,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62859307/

相关文章:

android - 如何从 fragment Kotlin Android动态设置单选按钮的高度

java - Intellij 找不到 JDK

android - 如何接收媒体音量变化通知?

android - 如何将 Firebase dataSnapshot 对象和 key 保存到 Kotlin DTO 数据类中?

Android LiveData转换: Changing LiveData object value

android - 使用 Picasso 加载位图

java - 实现我自己的有序双向链表 (Java)

java - 如何使用 Java 将文本从 JTextArea 发送到某些电子邮件

java - 构建 Java 语言的扩展 - 步骤?

android - 谷歌反向图像搜索 API