Исключение NotSerializableException: котлин.НЕИНИЦИАЛИЗИРОВАННОЕ ЗНАЧЕНИЕ после установки minifyEnabled true

#android #kotlin #gradle #delegates #serializable

#Android #kotlin #gradle #делегаты #сериализуемый

Вопрос:

После настройки minifyEnabled true в скрипте приложения build.gradle я начал получать это исключение:

 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
  

Произошел сбой при попытке перейти class SomeClass : Parcelable к другому действию.

Я пытался внести в белый список все классы приложений с помощью -keep class some.package.**.* { *; } , но безуспешно.

Ответ №1:

Я наткнулся на ту же проблему, и текущий ответ неверен: добавление @delegate:Transient в ленивом режиме приведет к аннулированию резервного поля при десериализации.

Поскольку проблема возникает только тогда, когда minifyEnabled = true это проблема Proguard / R8. Я решил это, добавив следующие строки в свой proguard-rules.pro :

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

Комментарии:

1. Я так рад, что нашел ваш ответ. Я столкнулся с точно такой же проблемой R8, и это устранило проблему.

2. Мне все еще любопытно, какой тип оптимизации мешает процессу десериализации. Кто-нибудь уже узнал?

Ответ №2:

Ленивый делегат использует UNINITIALIZED_VALUE объект за сценой. Используется для проверки, объявлена переменная или нет. Каким-то образом [нужно больше информации] ленивый делегат изменяет свое поведение во время минимизации кода. Это создает ситуацию, когда до тех пор, пока minifyEnabled отключен, передача объекта с отложенным инициализированным полем работает нормально без попыток его сериализации. Но после включения минимизации Java пытается сериализоваться UNINITIALIZED_VALUE , что вызывает исключение во время выполнения.

К сожалению, stacktrace не сообщает вам точно, какое поле в каком классе вы должны обновить. По крайней мере, это подскажет вам, какой из основных классов содержит ваш сломанный Serializable класс.

Давайте предположим, что в данном случае some.package.SomeClass содержит AnotherClass поле. Чтобы исправить это, вам нужно найти все поля ленивого класса, где classes реализует Serializable . Затем добавьте @delegate:Transient к ним, например.

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

Комментарии:

1. Это неправильно: добавление @delegate:Transient приведет к аннулированию резервного поля и вызовет NPE при доступе к нему.