Avatar ·

Proguard ругается на Missing classes после добавления пакетов в proguard-rules.pro

Всем привет! Уже несколько дней бьюсь над проблемой. Проект на Kotlin, использую обфускацию через R8 (Android Gradle Plugin 7.4+). Всё работало нормально, пока я не начал подключать несколько сторонних библиотек (Retrofit, Gson, Room). Я добавил в `proguard-rules.pro` правила для сохранения моделей данных: ``` -keep class com.myapp.data.model.** { *; } -keep class com.myapp.data.remote.** { *; } ``` И для Retrofit: ``` -keepattributes Signature -keepattributes *Annotation* -keep class retrofit2.** { *; } -keepclasseswithmembers class * { @retrofit2.http.* ; } ``` Но при сборке релизной версии (minifyEnabled true) получаю ошибки: ``` Missing classes detected while running R8: com.google.gson.internal.ConstructorConstructor com.google.gson.internal.bind.TypeAdapters com.google.gson.reflect.TypeToken ``` Пробовал добавить `-dontwarn com.google.gson.**` — не помогло. Пробовал прописать полные пути к классам Gson — тоже без результата. Версии: Gson 2.10.1, Retrofit 2.9.0, Room 2.6.1. Почему R8 не видит классы Gson, хотя я явно их не исключал? И как правильно настроить proguard-rules.pro, чтобы избежать таких ошибок? Подскажите, пожалуйста, куда копать. Заранее спасибо! <<>>

Причина проблемы и общее решение

Ошибка Missing classes detected while running R8 возникает, когда R8 не может найти классы, от которых зависят ваши библиотеки (Gson, Retrofit, Room), потому что они были удалены или переименованы в процессе обфускации. Это происходит, если вы не указали R8 сохранить эти классы, или если конфликтуют разные правила keep/dontwarn.

Основная причина в вашем случае — неполный набор правил для Gson. Gson использует рефлексию для сериализации/десериализации, и R8 по умолчанию удаляет классы, которые не вызываются напрямую из кода приложения. Добавление -keep class com.myapp.data.model.** сохраняет ваши модели, но не затрагивает внутренние классы Gson, такие как ConstructorConstructor или TypeAdapters.

Пошаговое исправление

  1. Добавьте официальные правила для Gson (они должны быть в документации библиотеки):
# Gson
-keepattributes Signature
-keepattributes *Annotation*
-keep class com.google.gson.** { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
  1. Добавьте правила для Retrofit и OkHttp (если используете OkHttp):
# Retrofit
-keepattributes Signature
-keepattributes Exceptions
-keep class retrofit2.** { *; }
-keepclasseswithmembers class * {
    @retrofit2.http.* <methods>;
}

# OkHttp (если есть)
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
  1. Добавьте правила для Room:
# Room
-keep class * extends androidx.room.RoomDatabase
-keep @androidx.room.Entity class *
-dontwarn androidx.room.paging.**
  1. Общие правила для Kotlin (чтобы не ломались корутины и сериализация):
# Kotlin
-keep class kotlin.Metadata { *; }
-keepclassmembers class * {
    @kotlin.Metadata <fields>;
}
-dontwarn kotlin.**
-keep class kotlinx.coroutines.** { *; }

Проверка и отладка

После добавления правил выполните чистую сборку:

./gradlew clean
./gradlew assembleRelease --stacktrace

Если ошибка осталась, проверьте, нет ли дублирующих правил -dontwarn, которые могут конфликтовать. Иногда помогает добавить:

-ignorewarnings

Но это крайняя мера — лучше разобраться с каждым предупреждением.

Дополнительный совет

Используйте consumer-rules.pro в своих библиотеках, чтобы R8 автоматически подхватывал правила. Для сторонних библиотек проверьте, есть ли у них файл proguard.txt внутри AAR — часто он уже содержит нужные правила. Если нет — копируйте их вручную.

В вашем случае основная проблема решится добавлением -keep class com.google.gson.** { *; }. Это сохранит все классы Gson, и R8 перестанет жаловаться на пропущенные зависимости.

<<>>

Альтернативный подход: использование consumer-rules.pro и проверка зависимостей

Если добавление правил в proguard-rules.pro не помогает, возможно, проблема в порядке подключения зависимостей или в конфликте версий. Попробуйте следующие шаги:

  1. Проверьте, какие версии Gson и Retrofit вы используете. Старые версии (например, Gson 2.8.x) могут требовать других правил. Обновитесь до последних стабильных версий:
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
  1. Включите consumer-rules.pro в модуле библиотеки. Если вы разрабатываете свою библиотеку, создайте файл consumer-rules.pro в корне модуля и укажите в build.gradle:
consumerProguardFiles 'consumer-rules.pro'

Внутри пропишите те же правила, что и в основном ответе. Это гарантирует, что при сборке приложения R8 автоматически применит их.

  1. Используйте --full-mapping для отладки. Сгенерируйте полный маппинг классов, чтобы понять, какие классы удаляются:
android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            // Добавьте для отладки:
            proguardFile 'proguard-rules.pro'
            testProguardFile 'proguard-test-rules.pro'
        }
    }
}

После сборки найдите файл build/outputs/mapping/release/mapping.txt и проверьте, какие классы Gson были переименованы или удалены. Это укажет на конкретные правила, которые нужно добавить.

Если ничего не помогает, временно отключите обфускацию для Gson через -keep class com.google.gson.** { *; } — это решит проблему, хотя и увеличит размер APK.