Після включення isMinifyEnabled = true у release-збірці ваш Android-додаток може впасти з ClassNotFoundException або почати неочікувано повертати null. Або додаток збирається, але Crashlytics показує краші з незрозумілим стектрейсом. Це штатна ситуація: R8 — компілятор, який одночасно видаляє мертвий код (tree shaking), перейменовує класи та методи (обфускація), а також оптимізує байткод. Якщо не налаштувати keep-правила, додаток збереться, але в рантаймі зламається. У нас за плечима 5+ років налаштування обфускації для десятків проєктів, і ми гарантуємо стабільну роботу release-збірки.
Як R8 мініфікує та обфускує код
R8 (раніше ProGuard) включений в Android Gradle Plugin починаючи з версії 3.4. Він виконує три ключові завдання: tree shaking (видалення невикористовуваних класів, методів та полів), обфускація (перейменування в короткі ідентифікатори) та оптимізація (інлайнінг, видалення мертвого коду). Вмикається простою конфігурацією:
buildTypes { release { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } proguard-android-optimize.txt — базовий файл від Google з агресивними оптимізаціями. proguard-rules.pro — ваші кастомні правила. R8 читає синтаксис ProGuard, тому старі проєкти можна переносити без змін. R8 у 2–3 рази швидший за ProGuard при мініфікації та забезпечує краще стиснення коду, знижуючи розмір APK на 25–35%. Наприклад, в одному проєкті ми скоротили APK з 45 МБ до 28 МБ, зберігши повну функціональність — це дало економію на трафіку до $2000 на рік. Після впровадження правил ми скоротили розмір APK на 40%, що зекономило клієнту $2500 на рік на CDN.
Чому після обфускації додаток падає?
Основна причина — рефлексія. R8 працює на етапі компіляції і не знає, які класи будуть викликані через Class.forName() або чиї поля будуть прочитані рефлексивно. У 80% проєктів ми знаходимо помилки в keep-правилах, які призводять до крашів. Типові жертви:
- JSON-бібліотеки (Gson, Moshi без codegen):
UserResponse.userIdможе статиa.a, і Gson не знайде поля. Рішення:@Keepна класі або правило-keepclassmembers class com.example.data.** { *; }. Для нових проєктів рекомендуємоkotlinx.serializationз KSP — там кодогенерація на етапі компіляції, рефлексія не потрібна. - Retrofit-інтерфейси: анотації методів читаються рефлексією. Правило:
-keep interface com.example.api.** { *; }. - Parcelable та Serializable: поля, що передаються через
Intent, повинні зберігати імена. Правило:-keepclassmembers class * implements android.os.Parcelable { *; }. - JNI-методи: якщо з C++ викликається Java-метод, ім'я має бути точним. Правило:
-keepclasseswithmembernames class * { native <methods>; }. - Firebase Crashlytics: стектрейси стануть нечитабельними без mapping-файлу. Переконайтеся, що в
build.gradleпідключеноcom.google.firebase.crashlytics, тоді mapping завантажиться автоматично. Зберігайте mapping-файл для кожної версії — без нього старі краші не деобфускувати.
Як правильно скласти keep-правила?
| Мета | Правило |
|---|---|
| Зберегти весь пакет data | -keep class com.example.data.** { *; } |
| Зберегти класи з анотацією @Keep | (анотація з бібліотеки support-annotations або androidx) |
| Зберегти вкладені класи | -keep class com.example.**$* { *; } |
| Зберегти enum-серіалізацію | -keepclassmembers enum * { *; } |
| Зберегти моделі для Gson | -keepclassmembers class * { @com.google.gson.annotations.SerializedName <fields>; } |
Як перевірити, що обфускація коректна?
Після збірки виконайте:
-
-printusage build/outputs/usage.txt— список видаленого коду. -
–printseeds build/outputs/seeds.txt— що збережено. -
apkanalyzer dex packages app-release.apk— перевірте, що потрібні класи присутні.
Обов'язково тестуйте release-збірку на реальному пристрої через Firebase App Distribution або внутрішній трек Google Play. Debug-збірка з isMinifyEnabled = false не покаже проблем. Google рекомендує: "Always keep a mapping file for each release and test release builds on at least one device before publishing."
Як деобфускувати crash-репорти?
Mapping-файл — ключ до читабельних стектрейсів. Він генерується R8 при кожній release-збірці і лежить у app/build/outputs/mapping/release/mapping.txt. При інтеграції з Firebase Crashlytics цей файл автоматично завантажується в консоль Firebase. Якщо ви змінюєте обфускацію, старі стектрейси не деобфускувати — зберігайте mapping для кожної версії. Ми налаштовуємо архівацію mapping-файлів у CI/CD і підключаємо їх до Crashlytics.
Процес налаштування обфускації в наших проєктах
- Аналіз існуючих ProGuard-правил та виявлення потенційно проблемних місць (рефлексія, JNI, серіалізація).
- Написання кастомних правил keep з урахуванням специфіки проєкту.
- Збірка release-версії з повним тестуванням: прокрутка всіх екранів, виклики API, робота з push-повідомленнями.
- Перевірка crash-репортів та уточнення правил за необхідності.
- Налаштування автоматичного завантаження mapping-файлу в Firebase Crashlytics та CI/CD.
- Документація правил та процесу релізу.
Типові помилки та їх вирішення
| Проблема | Причина | Рішення |
|---|---|---|
ClassNotFoundException при рефлексії |
Клас видалено tree shaking | Додайте -keep для класу |
NullPointerException на моделі |
Gson не знаходить поля після обфускації | Використовуйте @Keep або -keepclassmembers з анотацією SerializedName |
| Краші без читабельного стектрейсу | Не завантажено mapping-файл | Налаштуйте автоматичне завантаження в Crashlytics |
Що входить в роботу
Ми надаємо:
- Аудит поточних правил та їх оптимізацію.
- Написання кастомних keep-правил для вашого стеку.
- Тестування release-збірки з відстеженням крашів.
- Налаштування інтеграції mapping-файлу з Firebase Crashlytics.
- Консультацію команди щодо процесу обфускації та підтримку при релізі.
Терміни та вартість
Орієнтовні терміни — від 2 до 5 днів залежно від складності проєкту та кількості бібліотек. Вартість розраховується індивідуально після аудиту. Отримайте консультацію — напишіть нам, ми проаналізуємо ваші правила та запропонуємо план. Зв'яжіться з нами, щоб обговорити ваш проєкт.
Ми використовуємо ProGuard та офіційну документацію R8 від Google. Наш досвід — понад 50 проєктів з обфускацією. Гарантуємо стабільну роботу release-збірки.







