NotSerializableException: kotlin.UNINITIALIZED_VALUE after setting minifyEnabled true

Viewed 466

After setting minifyEnabled true in app build.gradle script I starded to receive this exception:

Caused by: java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = some.package.SomeClass)
    at android.os.Parcel.writeSerializable(Parcel.java:1767)
    …
Caused by: java.io.NotSerializableException: kotlin.UNINITIALIZED_VALUE

Crash occurred when I try to pass class SomeClass : Parcelable to another activity.

I tried to whitelist all app classes with -keep class some.package.**.* { *; } but without succeed.

2 Answers

I stumbled upon the same issue, and the current answer is wrong: adding @delegate:Transient on the lazy will nullify the backing field on deserialization.

Since the issue is only occuring when minifyEnabled = true, that's a Proguard/R8 issue. I solved it by adding the following lines to my proguard-rules.pro:

-keep class * implements kotlin.Lazy {
    *;
}

Lazy delegate use UNINITIALIZED_VALUE object behind the scene. It's used to check if variable is declared or not. In some way [need more info] lazy delegate change their behavior during code minification. It creates situation where as long as minifyEnabled is disabled then passing object with lazy initialized field works fine without try to serialize it. But after enable minify Java tries to serializable UNINITIALIZED_VALUE which throws exception in runtime.

Unfortunately stacktrace don't tell you exactly which field in which class you must update. At least it tell you which of main class contains your broken Serializable class.

Let's assume that in this case some.package.SomeClass contains AnotherClass field. To fix it you need to find all lazy class fields where classes implements Serializable. Then add @delegate:Transient to them, eg.

class AnotherClass: Serializable {

    …

    @delegate:Transient // <- add this
    val myLazyField by lazy { "Kotlin, why?!" }
}
Related