ProGuard/R8: суть, обфускация и защита Android приложений

Автор: IT Sectr Опубликовано: 2026-02-14 Время чтения: 8 мин

ProGuard и R8 — инструменты обфускации, минификации и оптимизации для Android-приложений. ProGuard, созданный в 2002 году, долгое время был стандартом де-факто для защиты Java-кода. R8 — его преемник, разработанный Google и встроенный в Android Gradle Plugin начиная с AGP 3.4. Оба инструмента уменьшают размер APK, удаляют мёртвый код и усложняют reverse engineering. По данным Android Developers, R8 выполняет сборку в 2–3 раза быстрее ProGuard при сопоставимом качестве обфускации.

Главное

  • ProGuard — инструмент обфускации и оптимизации Java-байткода, стандарт для Android с 2000-х годов
  • R8 — преемник ProGuard от Google, встроенный в AGP, выполняет обфускацию, минификацию и оптимизацию за один проход
  • Обфускация переименовывает классы и методы в короткие имена, усложняя reverse engineering приложения
  • Минификация удаляет неиспользуемые классы, методы и поля, уменьшая размер итогового APK/AAB
  • ProGuard rules (.pro файлы) управляют тем, какие части кода сохраняются, а какие обфусцируются или удаляются

Что такое ProGuard?

ProGuard — это инструмент с открытым исходным кодом (Apache 2.0) для обфускации, минификации, оптимизации и преверификации Java-байткода. Разработан Эриком Лафоржем в 2002 году в рамках проекта SourceForge. ProGuard принимает на вход скомпилированные Java-классы (.class) либо JAR-архивы и выдаёт обработанные классы того же формата, но меньшего размера и с переименованными элементами.

Долгое время ProGuard был единственным стандартом для защиты Android-приложений от reverse engineering. Google официально рекомендовала его использование в Android SDK и поставляла конфигурацию по умолчанию в файле proguard-android-optimize.txt внутри SDK tools. ProGuard работал как отдельный инструмент, запускаемый после компиляции Java-кода в байткод и перед упаковкой в DEX.

Архитектура ProGuard

ProGuard состоит из четырёх последовательных фаз: shrink (удаление неиспользуемых классов), optimize (оптимизация байткода — инлайнинг, удаление мёртвого кода), obfuscate (переименование классов, методов и полей в короткие имена), preverify (проверка совместимости с JVM). Каждая фаза управляется отдельными правилами из конфигурационных файлов.

На этапе обфускации ProGuard генерирует mapping-файл (mapping.txt), который отображает исходные имена на обфусцированные. Этот файл критически важен для декодирования crash-логов из релизных сборок через утилиту retrace. Без mapping-файла stack trace превращается в набор букв a(), b(), c() без возможности восстановления исходного контекста.

Фаза ProGuardНазначениеРезультат
ShrinkАнализ графа вызовов и удаление мёртвого кодаУменьшение количества классов в APK
OptimizeИнлайнинг методов, удаление неиспользуемых параметровУскорение выполнения кода
ObfuscateПереименование классов, полей и методовЗащита от reverse engineering
PreverifyДобавление StackMap-атрибутов для JVMСовместимость с Java 6+

Что такое R8?

R8 — это инструмент обфускации иминификации следующего поколения от Google, впервые представленный в Android Studio 3.3 (ноябрь 2018) и ставший стандартным в AGP 3.4 (август 2019). В отличие от ProGuard, R8 является частью компилятора D8/R8, который преобразует Java-байткод в DEX-формат. R8 выполняет все фазы — обфускацию, минификацию и оптимизацию — за один проход, без передачи промежуточных файлов между инструментами.

Google разработала R8 с двумя целями: ускорить сборку (ProGuard работал как внешний инструмент) и обеспечить бесшовную интеграцию с современным Android-стеком (Desugar, Core Library Desugaring, D8). R8 написан на Kotlin и Java и является частью репозитория R8/Desugar на AOSP (Android Open Source Project).

Важное преимущество R8 — полная обратная совместимость с ProGuard rules. Существующие .pro-файлы работают без изменений. R8 даже поддерживает специфичные ProGuard-директивы, включая -whyareyoukeeping, -printconfiguration и -printmapping. Это означает, что переход с ProGuard на R8 происходит прозрачно: достаточно обновить AGP.

kotlin
// build.gradle.kts — включение R8 через minifyEnabled
android {
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            isShrinkResources = true

            proguardFiles(
                // Базовая конфигурация из Android SDK
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // Пользовательские правила проекта
                "proguard-rules.pro"
            )
        }
    }
}

Код демонстрирует стандартную конфигурацию release-сборки. Флаг isMinifyEnabled = true активирует R8 для обфускации и оптимизации. isShrinkResources = true дополнительно удаляет неиспользуемые ресурсы. getDefaultProguardFile загружает базовые правила из SDK, а proguard-rules.pro содержит специфичные для проекта настройки.

Обфускация кода в Android

Обфускация — это процесс преобразования исходного кода в форму, которую сложно анализировать человеку, но которая сохраняет полную функциональность. В контексте Android обфускация означает переименование классов, методов и полей в короткие, лишённые смысла имена: com.example.app.auth.LoginManager превращается в a.a.a, метод authenticateUser — в a, поле userToken — в b.

Зачем нужна обфускация

APK-файлы Android — это архивы, которые можно открыть любым архиватором (ZIP, 7z, WinRAR). Без обфускации злоумышленник получает полную карту приложения: названия пакетов, классов, методов и полей. Инструменты вроде jadx или Bytecode Viewer восстанавливают почти исходный Java-код из DEX-файлов за секунды. Обфускация не делает код неуязвимым, но существенно повышает порог входа: вместо осмысленных имен читатель видит a(), b(), c().

Типовые цели обфускации: защита коммерческой логики (алгоритмы, формулы расчёта), затруднение кражи API-ключей и токенов, предотвращение подмены классов через reflection, защита от патчинга и модификации APK (repackage attack). На практике 70% задач решает именно переименование — ради этого и запускают ProGuard / R8.

Пример ProGuard правил

Ниже приведён типовой файл proguard-rules.pro для Android-проекта с Retrofit, Gson и Parcelable. Правила -keep сохраняют классы и методы, необходимые для работы библиотек через reflection. Без этих правил R8 удалит или переименует классы, к которым обращается библиотека по имени строки.

pro
# =====================
# Retrofit — сохранение интерфейсов
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions

# =====================
# Gson — сериализация JSON
# =====================
-keepclassmembers class * {
    @com.google.gson.annotations.SerializedName <fields>;
}
-keep class *.serialization.** {
    <fields>;
}

# =====================
# Parcelable — Creator
# =====================
-keepclassmembers class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator CREATOR;
}

# =====================
# Logging — удаление логов из релиза
# =====================
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(String, int);
    public static int v(...);
    public static int d(...);
    public static int i(...);
    public static int w(...);
    public static int e(...);
}

# =====================
# Дата-классы Kotlin — сохраняем конструторы
# =====================
-keepclassmembers class * {
    @kotlin.Metadata <fields>;
}

# =====================
# Activity — точка входа
# =====================
-keep class * extends android.app.Activity {
    @android.annotation.SuppressLint <methods>;
}

Каждая директива в .pro файле решает конкретную задачу. -keep предотвращает удаление или переименование класса целиком. -keepclassmembers защищает только члены класса (поля и методы), но позволяет удалить сам класс, если он не используется. -assumenosideeffects указывает R8, что вызов метода не имеет побочных эффектов и может быть безопасно удалён. Директива -keepattributes сохраняет метаданные в байткоде — аннотации, сигнатуры, исключения.

Правило -keep,allowobfuscation,allowshrinking для Retrofit разрешает R8 переименовывать интерфейсы, но не удалять их. Это необходимо, так как Retrofit обращается к интерфейсам через динамический прокси (java.lang.reflect.Proxy), и удаление приведёт к ClassNotFoundException в runtime. Аналогично, Gson использует reflection для доступа к полям с аннотацией @SerializedName — без -keepclassmembers поля будут удалены как неиспользуемые.

Минификация и ShrinkResources

Минификация (shrinking) — процесс удаления неиспользуемого кода и ресурсов из итоговой сборки. ProGuard и R8 анализируют граф вызовов, начиная с точек входа (Activity, Service, BroadcastReceiver), и удаляют классы и методы, до которых нельзя дойти по цепочке вызовов. ShrinkResources — дополнительный этап, который удаляет неиспользуемые ресурсы из res/ (layout, drawable, string, color).

Минификация даёт наибольший выигрыш в больших проектах с библиотеками. Типовая картина: проект использует 10% кода из подключённой библиотеки (например, Google Play Services). Без минификации весь код библиотеки попадает в APK. С минификацией R8 удаляет 70–90% кода библиотек, оставляя только реально используемые классы методы. Это напрямую влияет на размер APK, время загрузки и потребление памяти.

ShrinkResources в действии

Механизм ShrinkResources работает в паре с минификацией кода. После того как R8 определил, какие классы используются, ресурсный shrinking анализирует ссылки на ресурсы из кода: R.layout.main, R.drawable.icon, getString(R.string.title). Все ресурсы, на которые нет прямой или косвенной ссылки, удаляются из финального APK или AAB. Для этого используется ресурсный файл resources.arsc и папки res/.

Важный нюанс: ресурсы могут быть вызваны через getIdentifier() или Resources.getResourceName() по строковому имени, минуя R-класс. В таких случаях R8 не видит прямой связи и может удалить ресурс, который фактически используется. Для защиты таких ресурсов существует директива -keep class **.R$* { *; } — она сохраняет все идентификаторы R-класса.

xml
<!-- Пример: ресурс, который используется только через getIdentifier() -->
<string name="dynamic_title_welcome">Добро пожаловать</string>
<string name="dynamic_title_share">Поделиться</string>

<!-- Kotlin-код, который обращается по строке -->
<!-- val title = getString(resources.getIdentifier( -->
<!--     \"dynamic_title_${type}\", \"string\", packageName)) -->

В этом случае R8 не видит статической ссылки на dynamic_title_welcome в R-классе, потому что обращение происходит через getIdentifier с динамическим именем. Чтобы сохранить такие ресурсы, нужно добавить в proguard-rules.pro директиву -keepclassmembers class **.R$string { *; } — она запрещает удаление любых полей из всех R$string классов.

ДирективаНазначениеПример
-keepСохраняет класс и все его члены-keep class com.example.api.** { *; }
-keepclassmembersСохраняет только члены класса-keepclassmembers class * { @SerializedName <fields>; }
-keepattributesСохраняет метаданные байткода-keepattributes *Annotation*, Signature
-assumenosideeffectsУдаляет вызовы без побочных эффектов-assumenosideeffects class Log { d(...); }
-dontwarnПодавляет предупреждения-dontwarn com.example.legacy.**

R8 vs ProGuard: ключевые отличия

Несмотря на то что R8 является преемником ProGuard, между инструментами есть принципиальные различия в архитектуре, производительности и поведении. Google официально прекратила поддержку ProGuard в Android Gradle Plugin начиная с AGP 7.0, однако ProGuard продолжает использоваться в проектах, где требуется специфическое поведение оптимизации, недоступное в R8.

Сравнительная таблица

ХарактеристикаProGuardR8
РазработчикGuardSquare (Эрик Лафорж)Google
Год выхода20022018 (стабильный в 2019)
Архитектура4 отдельные фазы (shrink → optimize → obfuscate → preverify)Один проход: shrink + optimize + obfuscate одновременно
Интеграция в AGPВнешний инструмент, запускается после javacВстроен в D8 компилятор DEX
Скорость сборкиМедленнее в 2–3 разаБыстрее за счёт одного прохода и нативной интеграции
Поддержка KotlinОграниченная (проблемы с inline, lambdas, coroutines)Полная: coroutines, inline-функции, data class
Mapping-файлmapping.txt (совместим с retrace)mapping.txt (тот же формат)
Кастомизация оптимизации60+ опций -optimizationpasses, -optimizationsОграниченная: большинство оптимизаций включены по умолчанию
Статус поддержкиЗаменён на R8 (AGP 7.0+ не использует)Активная разработка, часть AOSP

Когда R8 может сломать сборку

R8 агрессивнее ProGuard удаляет код, который считает мёртвым. Это приводит к ситуациям, когда сборка в debug работает, а release падает с ClassNotFoundException или NoSuchMethodException. Типовые случаи: библиотеки, использующие reflection по имени класса (Gson, Moshi, Retrofit, Room, Dagger); вызовы ServiceLoader или java.util.ServiceLoader; динамические прокси (java.lang.reflect.Proxy); нативные методы (JNI). Решение — добавить -keep для всех классов, которые вызываются через reflection.

pro
# Типовые проблемы reflection — R8 не видит статической связи

# Room — сохраняем DAO и миграции
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }

# Dagger / Hilt — сохраняем компоненты
-keep class * extends dagger.hilt.android.components.** { *; }

# JNI — не переименовываем нативные методы
-keepclasseswithmembernames class * {
    native <methods>;
}

# Data Binding — сохраняем Binding-классы
-keep class *.databinding.** { *; }

Если после добавления правил сборка всё ещё падает, используйте флаг -printconfiguration full-config.txt в proguard-rules.pro. R8 сгенерирует полный файл конфигурации, который показывает, какие правила применены и какие классы сохраняются. Также полезна директива -whyareyoukeeping class com.example.MyClass — она выводит причину, по которой R8 решил сохранить данный класс.

Настройка ProGuard rules

Правильная настройка ProGuard rules — ключ к стабильной работе обфускации без багов в runtime. Ниже приведён пошаговый процесс настройки для нового проекта или для проекта, в котором обфускация вызывает ошибки.

Шаг 1: Базовая конфигурация

Начните с подключения стандартного файла Android SDK — proguard-android-optimize.txt. Он содержит правила для базовых компонентов Android: Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. Этот файл находится в папке SDK: $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Если вы используете AGP, getDefaultProguardFile загрузит его автоматически.

Шаг 2: Библиотеки

Каждая популярная библиотека имеет рекомендованные ProGuard rules. Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — все они требуют специфических -keep правил. Обычно правила включены в AAR-библиотеку и подключаются автоматически через consumer guard rules. Проверьте, что библиотека предоставляет файл proguard.txt внутри AAR — это признак того, что правила уже учтены.

Шаг 3: Тестирование релизной сборки

Перед публикацией обязательно протестируйте релизную сборку на реальном устройстве или эмуляторе. Проблемы обфускации проявляются только в runtime. Проверьте: авторизацию (логин/регистрацию), загрузку данных из сети, навигацию между экранами, камеру и галерею, push-уведомления, Deeplinks, WebView. Каждый краш в релизной сборке нужно декодировать через retrace с mapping-файлом и добавлять недостающие -keep правила.

Шаг 4: Mapping-файл и CI

Mapping-файл генерируется в build/outputs/mapping/release/mapping.txt. Этот файл обязателен для сохранения: без него невозможно декодировать crash-логи из Google Play Console. Включите mapping.txt в систему контроля версий или загружайте его в CI-артефакты. Google Play Console принимает mapping-файл автоматически при загрузке AAB с включённым uploading mapping.txt.

Ниже приведён полный workflow настройки обфускации в файле proguard-rules.pro с комментариями для каждой группы правил.

pro
# ===========================================
# proguard-rules.pro — полный пример
# ===========================================

# --- Общие настройки ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify

# --- Android компоненты ---
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.Fragment
-keep public class * extends androidx.fragment.app.Fragment
-keep public class * extends android.view.View

# --- OkHttp / Retrofit ---
-dontwarn okhttp3.**
-dontwarn okio.**
-keep class retrofit2.** { *; }
-keepattributes Exceptions

# --- Gson / Moshi ---
-keepclassmembers class * {
    @com.google.gson.annotations.SerializedName <fields>;
}
-keep class com.google.gson.** { *; }

# --- Firebase ---
-keep class com.google.firebase.** { *; }
-keep class com.google.android.gms.** { *; }

# --- Kotlin Coroutines ---
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}

# --- Сериализация ---
-keepclassmembers class * implements java.io.Serializable {
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

# --- R8 только: принудительное сохранение ---
# (ProGuard игнорирует эту директиву)
-keep,allowobfuscation class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator CREATOR;
}

После настройки выполните сборку: ./gradlew assembleRelease. Проверьте, что в build/outputs/mapping/release/ появились файлы: mapping.txt (соответствие исходных и обфусцированных имён), seeds.txt (классы, сохранённые правилами -keep), usage.txt (классы, удалённые при минификации). Размер APK после обфускации должен уменьшиться на 20–50% в зависимости от количества подключённых библиотек.

Часто задаваемые вопросы

Чем R8 отличается от ProGuard?

R8 — преемник ProGuard, разработанный Google. R8 выполняет обфускацию, минификацию и оптимизацию за один проход, работает в 2–3 раза быстрее ProGuard и интегрирован напрямую в Android Gradle Plugin. ProGuard использует четыре отдельные фазы и требует внешнего запуска. Начиная с AGP 7.0 ProGuard не используется — по умолчанию работает R8.

Нужно ли писать ProGuard rules при использовании R8?

Да, R8 использует те же ProGuard rules (.pro файлы). Директивы -keep, -keepclassmembers, -keepattributes, -assumenosideeffects работают идентично. Базовые правила идут в proguard-android-optimize.txt из Android SDK, а специфичные для библиотек (Retrofit, Room, Gson) добавляются в proguard-rules.pro проекта. Без этих правил R8 может удалить классы, необходимые для работы библиотек через reflection.

Как включить R8 в Android проекте?

R8 включён по умолчанию в Android Gradle Plugin начиная с AGP 3.4. Для активации минификации установите isMinifyEnabled = true в блоке release buildType файла build.gradle.kts. Дополнительный флаг isShrinkResources = true включает удаление неиспользуемых ресурсов. В gradle.properties можно принудительно отключить R8 через android.enableR8=false, но это не рекомендуется — R8 быстрее и стабильнее.

Что такое обфускация кода в Android?

Обфускация — переименование классов, методов и полей в короткие бессмысленные имена (a, b, c). Класс com.example.app.auth.LoginManager превращается в a.a.a, метод authenticateUser — в a. Это усложняет reverse engineering приложения, но не влияет на логику выполнения. ProGuard и R8 переименовывают только те элементы, которые не защищены правилами -keep. Mapping-файл сохраняет соответствие исходных и обфусцированных имён для декодирования crash-логов.

Как отладить crash-лог из обфусцированного приложения?

Для декодирования stack trace используется утилита retrace (входит в состав ProGuard/R8 SDK). Команда: retrace mapping.txt crash-stacktrace.txt. Mapping-файл находится в build/outputs/mapping/release/mapping.txt. Google Play Console также поддерживает загрузку mapping.txt при публикации AAB — crash-логи автоматически декодируются в консоли. Без mapping-файла stack trace будет содержать только обфусцированные имена a.b.c(), что бесполезно для отладки.

Итоги

  • ProGuard — классический инструмент обфускации и оптимизации Java-байткода, состоящий из четырёх последовательных фаз
  • R8 — современный преемник от Google, встроенный в AGP, выполняющий все фазы за один проход с производительностью в 2–3 раза выше
  • Обфускация переименовывает классы, методы и поля в короткие имена, усложняя reverse engineering и защищая коммерческую логику приложения
  • Минификация удаляет неиспользуемый код и ресурсы, уменьшая размер APK на 20–50% в типовых проектах
  • ProGuard rules (.pro файлы) управляют поведением обфускации — директивы -keep, -keepclassmembers, -assumenosideeffects задают, какие элементы сохраняются, удаляются или переименовываются
  • Mapping-файл (mapping.txt) — критически важный артефакт сборки для декодирования crash-логов из релизных сборок через retrace
  • Тестирование релизной сборки на реальном устройстве обязательно — проблемы обфускации проявляются только в runtime и требуют добавления недостающих -keep правил

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также