5

I have a package with some data classes and I'm trying to access the constructor at runtime using Kotlin reflection clazz.primaryConstructor, Everything is working as expected but when I enable R8, data classes metadata are removed so for example when I check if the KClass isData it returns false and the primary constructor is also null which happens only when enabling R8. I tried everything including adding the @keep annotation to all data classes and adding a rule to keep everything in the models package, I also added these rules

-keep class kotlin.reflect.**

-keep class kotlin.Metadata { *; }

but still no luck, any idea what is going wrong or how to fix this ?

Sample Repo

Thanks in advance.

David Rawson
  • 20,912
  • 7
  • 88
  • 124
Ahmad Mahmoud Saleh
  • 169
  • 1
  • 4
  • 15

1 Answers1

18

What turned out to be the issue is that R8 bundeled with AGP 7.0 (release with Android Studio - Arctic Fox) does not handle Kotlin metadata correctly for Kotlin 1.6.0.

If using Kotlin 1.6.0 (classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0") with AGP 7.0, then R8 version 3.0.77 is required for shrinking Kotlin libraries and for using kotlin-reflect. Updating to AGP 7.0.4 is not enough, as that version is bundled with R8 3.0.76.

To use R8 3.0.77 add the following to your settings.gradle or settings.gradle.kts:

pluginManagement {
    buildscript {
        repositories {
            mavenCentral()
            maven {
                url = uri("https://storage.googleapis.com/r8-releases/raw")
            }
        }

        dependencies {
            classpath("com.android.tools:r8:3.0.77")
            classpath('com.google.guava:guava:30.1.1-jre')
        }
    }
}

Another option for AGP 7.0 is to use Kotlin 1.5.31.

Also consider aligning your version of kotlin-reflect with the version of the Kotlin compiler.

sgjesse
  • 3,793
  • 14
  • 17
  • Thanks for answering @sgjesse, but I'm still facing the same issue, It feels like R8 is ignoring the rules because `clazz.primaryConstructor` is null only when enabling R8, also `clazz.isData` is false. – Ahmad Mahmoud Saleh Dec 13 '21 at 17:53
  • Which version of Android Studio / Android Gradle Plugin are you using? Are you using the default `proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')` for standard rules? I created a small sample project with a `data` class annotated with `androidx.annoation.Keep`, and that worked with `kotlin-reflect` (tested `isData` and `primaryConstructor`). The sample project is https://github.com/sgjesse/KotlinDataClasses. The additional keep rules I mentioned are not required, as they are provided by `kotlin-reflect`. – sgjesse Dec 14 '21 at 08:58
  • Thank you for taking the time to create the sample. I was not able to run it so I have created a sample Android app and copied you code but I'm still facing the same issue. https://github.com/ahmadmssm/DataClassR8 – Ahmad Mahmoud Saleh Dec 14 '21 at 21:45
  • 1
    Thank you for the sample. I figured out that the issue was related to Kotlin 1.6.0, and updated the answer above, hope that solves the issue for you. One other comment: In you sample you use `kotlin-reflect` 1.5.31, you should probably align that with you Kotlin version as well. – sgjesse Dec 15 '21 at 14:04
  • Thank you very much, the Kotlin version point did the trick, The sample Android project worked but for an unknown reason I'm still facing the same issue in my main project. – Ahmad Mahmoud Saleh Dec 15 '21 at 21:59
  • Thank you very much, Now it worked after unifying the Kotlin version plugin with the reflect library . – Ahmad Mahmoud Saleh Dec 15 '21 at 22:16
  • 1
    Thank you for reporting back. I added a comment on aligning the version of kotlin-reflect with the version of the Kotlin compiler to the answer. – sgjesse Dec 16 '21 at 14:51
  • 1
    I came here with the same issue but was running AGP 7.2.2. I updated to AGP 7.4.2 (gradle 7.5) and I was able to fix this issue too. thanks! I used the same proguard rules as the original post – Stefan Indaco Apr 20 '23 at 22:25