Android Enum: сгенерировать двоичный файл AAR с включенным R8 и выдает исключение "Function invocation 'name ()'" в клиентском приложении - PullRequest
2 голосов
/ 12 июля 2020

Это довольно редкий случай, и я пробовал погуглить, но, к сожалению, ничего не вышло.

В основном я хотел создать модуль библиотеки и собрать из него двоичный файл aar, чтобы использовать его в различных клиентских приложениях. Но одна странная вещь происходит, когда я создал класс перечисления в модуле библиотеки:

Когда R8 minifyEnabled выключен, сгенерированный файл aar можно импортировать в клиентское приложение и правильно компилировать. Но когда minifyEnabled включен, он начинает выдавать эту ошибку при вызове свойства enum ".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
    }
}

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, я заметил что когда minifyEnabled выключен в модуле lib, класс ScaleType транслируется в

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

, поэтому в клиентском приложении это абсолютно нормально, потому что это всего лишь способ вызова name kotlin.

ScaleType.LARGE.name //compile pass

но когда minifyEnabled имеет значение 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.

Это вызывает большие проблемы. Для приложения, которое напрямую импортирует библиотечный модуль, можно называть ".name". Но для приложений, использующих двоичный файл aar, они должны вызывать «.name ()», и, что еще более неприятно, вам все равно придется использовать «.name» при отладке модуля lib.

Любая помощь будет принята с благодарностью.

Если вы хотите попробовать, вы можете найти образец кода здесь

1 Ответ

0 голосов
/ 14 июля 2020

Компилятор Kotlin использует метаданные Kotlin в файлах классов, чтобы определить, что является свойством Kotlin. Следовательно, если метаданные Kotlin не поддерживаются, компилятор Kotlin не сможет определить, что name является свойством, и он будет рассматривать его как код Java и поэтому будет настаивать на том, чтобы вы вызывали name() method.

R8 добавила поддержку для сохранения и перезаписи Kotlin метаданных в AGP 4.1 beta3. Поэтому, если вы обновитесь до этой версии и поместите следующее в файл конфигурации сжатия библиотеки, надеюсь, это должно решить вашу проблему.

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