Сборка release падает на разборе JSON, а debug работает нормально
Экран разбирает JSON-ответ в модельный класс. В debug всё работает, а сборка release либо падает, либо молча отдаёт модель, у которой все поля null или нули. Из Kotlin к свойствам модели никто не обращается — до них добирается рефлексией сама JSON-библиотека. Считайте, что JSON в обеих сборках одинаков.
// app/build.gradle.kts
android {
buildTypes {
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
}
}
}
// Response.kt — создаётся только JSON-библиотекой, через рефлексию
data class Response(val id: Long, val title: String, val price: Double)
val response: Response = gson.fromJson(body, Response::class.java)
Определите, почему страдает только вариант release, и исправьте это.
Только в release включён isMinifyEnabled, поэтому R8 работает только там. Статически к членам модели никто не обращается, R8 считает их мёртвыми и переименовывает или удаляет; поиск рефлексией падает. Чинится keep-правилом или @Keep, а не отключением минификации.
- ✗Винить JSON или сетевой слой вместо прохода
R8, который есть только вrelease - ✗Чинить установкой
isMinifyEnabled = false, выбрасывая всё сжатие целиком - ✗Считать, что
R8не тронет класс, раз до него доходят рефлексией в рантайме
- →Почему
@Keepобычно лучше рукописного keep-правила с подстановкой? - →Что подсказал бы mapping-файл по стектрейсу release-сборки?
Почему падает только release
isMinifyEnabled = true стоит только в release, значит R8 запускается только там. Это первое, что стоит проверить: если баг живёт ровно в одном варианте, ищите то, что этот вариант делает дополнительно.
R8 обходит граф достижимого от keep-правил и точек входа. Модель Response создаётся только JSON-библиотекой по имени класса — статически её конструктор и свойства не вызывает никто. С точки зрения R8 это мёртвый код: свойства переименовываются в a, b, c или удаляются целиком. Рефлексия ищет поле title, не находит его и оставляет null.
// Вариант 1 — аннотация @Keep (androidx.annotation)
@Keep
data class Response(val id: Long, val title: String, val price: Double)
Второй вариант — keep-правило в proguard-rules.pro на пакет с моделями. Оба сохраняют имена; isMinifyEnabled = false тоже «чинит» симптом, но ценой отказа от сжатия и обфускации всей сборки — так делать нельзя.