Migrate to the new kotlin-parcelize

Viewed 10156

Can anyone please explain how to migrate to the new kotlin-parcelize?

I tried:

  1. Replace apply plugin: 'kotlin-android-extensions' with apply plugin: 'kotlin-parcelize' in app build.gradle
  2. Replace import kotlinx.android.parcel.Parcelize with import kotlinx.parcelize.Parcelize

The latter one leads to

Class 'ParcelZonedDateTime' is not abstract and does not implement abstract member public abstract fun describeContents(): Int defined in android.os.Parcelable

on e.g. this code:

import androidx.annotation.Keep
import kotlinx.parcelize.Parcelize
import org.threeten.bp.ZonedDateTime

@Keep
@Parcelize
data class ParcelZonedDateTime(var value: ZonedDateTime?) :Parcelable {
    override fun toString(): String {
        return value.toString()
    }
}

So, how to migrate to the new kotlin-parcelize?

Update:

Based on comments: Yes, I am on Kotlin 1.4.20 (which is newer than 1.4.20-M2).Kyzer is right that the error in the IDE (is not abstract and does not implement abstract member public abstract fun describeContents()) can and has to be ignored. Yet I have an issue where I use Generics:

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

sealed class MyDialogEvent {
    @Parcelize
    data class Confirmed<T: Parcelable>(val identity: String, val data: T) : Parcelable
}

The generated code for this is

@kotlin.Metadata(mv = {1, 4, 1}, bv = {1, 0, 3}, k = 3)
public static final class Creator implements android.os.Parcelable.Creator<com.example.stackoverflow.MyDialogEvent.Confirmed> {
    
    public Creator() {
        super();
    }
    
    @org.jetbrains.annotations.NotNull()
    @java.lang.Override()
    public final com.example.stackoverflow.MyDialogEvent.Confirmed<T>[] newArray(int size) {
        return null;
    }
    
    @org.jetbrains.annotations.NotNull()
    @java.lang.Override()
    public final com.example.stackoverflow.MyDialogEvent.Confirmed<T> createFromParcel(@org.jetbrains.annotations.NotNull()
    android.os.Parcel in) {
        return null;
    }
}

and during compilation I get this error for it (line numbers won't match the sample code, but you can easily identify the matching lines by comparing the method names):

MyDialogEvent.java:167: error: non-static type variable T cannot be referenced from a static context
            public final com.example.stackoverflow.MyDialogEvent.Confirmed<T>[] newArray(int size) {
                                                                                       
MyDialogEvent.java:173: error: non-static type variable T cannot be referenced from a static context
            public final com.example.stackoverflow.MyDialogEvent.Confirmed<T> createFromParcel(@org.jetbrains.annotations.NotNull()
6 Answers

That's a bug in kapt and it's reported here. Actually the java code you posted is a stub class which generated by kapt to run annotation processors on it (as you can see, there is no real implementation, just the structure and annotations). So as a workaround you can move the code with @Parcelize annotations to a separate module where you don't have kapt enabled. kotlin-parcelize itself does not use annotation processors to generate code, it uses a compiler plugin to emit the IR directly I believe.

Update: the issue is fixed in kotlin 1.5

This is work for me, please check the order of lines carefully

plugins {
    id 'kotlin-android'
    id 'kotlin-parcelize'
    id 'kotlin-kapt'    
   }

After further checking, I think Parcelable generic type with Parcel constructing fails to compile because T class can not be determined generically based from this reference.

enter image description here

Perhaps an enum could be the last resort.

I use the Kotlin version 1.4.10:

implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.10"

but in my module, only apply

id 'kotlin-android'
id 'kotlin-android-extensions'

and it work for me.

There is all my plugins apply in my module:

plugins {
    id 'com.android.library'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
    id 'androidx.navigation.safeargs.kotlin'
}

Answer for original question:

If you see this error in Android Studio:

Class 'ClassName' is not abstract and does not implement abstract member public abstract fun describeContents(): Int defined in android.os.Parcelable

Make sure that you have updated both Kotlin dependency and Android Studio Kotlin plugin to 1.4.20 or later. enter image description here After updating the plugin make sure to File -> Invalidate Caches / Restart. That worked for me.

change this line: import kotlinx.parcelize.Parcelize to: import kotlinx.android.parcel.Parcelize

Related