ProGuard/R8: Ofuscación y Protección de Aplicaciones Android

Autor: IT Sectr Publicado: 2026-02-14 Tiempo de lectura: 8 min

ProGuard y R8 son herramientas de ofuscación, minificación y optimización para aplicaciones Android. ProGuard, creado en 2002, fue durante mucho tiempo el estándar de facto para la protección de código Java. R8 es su sucesor, desarrollado por Google e integrado en Android Gradle Plugin desde AGP 3.4. Ambas herramientas reducen el tamaño del APK, eliminan código muerto y dificultan la ingeniería inversa. Según Android Developers, R8 realiza compilaciones 2–3 veces más rápido que ProGuard con una calidad de ofuscación comparable.

Puntos Clave

  • ProGuard — herramienta de ofuscación y optimización de bytecode Java, estándar para Android desde los 2000
  • R8 — sucesor de ProGuard de Google, integrado en AGP, realiza ofuscación, minificación y optimización en un solo paso
  • Ofuscación renombra clases y métodos con nombres cortos, dificultando la ingeniería inversa de la aplicación
  • Minificación elimina clases, métodos y campos no utilizados, reduciendo el tamaño final del APK/AAB
  • Reglas de ProGuard (archivos .pro) controlan qué partes del código se conservan, ofuscan o eliminan

¿Qué es ProGuard?

ProGuard es una herramienta de código abierto (Apache 2.0) para ofuscación, minificación, optimización y preverificación de bytecode Java. Fue desarrollado por Eric Lafarge en 2002 como parte del proyecto SourceForge. ProGuard toma clases Java compiladas (.class) o archivos JAR como entrada y produce clases procesadas del mismo formato, pero de menor tamaño y con elementos renombrados.

Durante mucho tiempo, ProGuard fue el único estándar para proteger aplicaciones Android de la ingeniería inversa. Google recomendó oficialmente su uso en Android SDK y proporcionó una configuración predeterminada en el archivo proguard-android-optimize.txt dentro de las herramientas del SDK. ProGuard funcionaba como una herramienta separada, ejecutada después de la compilación del código Java a bytecode y antes del empaquetado en DEX.

Arquitectura de ProGuard

ProGuard consta de cuatro fases secuenciales: shrink (eliminación de clases no utilizadas), optimize (optimización de bytecode — inlining, eliminación de código muerto), obfuscate (renombrado de clases, métodos y campos a nombres cortos), preverify (verificación de compatibilidad con JVM). Cada fase se controla mediante reglas separadas de los archivos de configuración.

Durante la etapa de ofuscación, ProGuard genera un archivo de mapeo (mapping.txt) que asigna los nombres originales a los ofuscados. Este archivo es crítico para decodificar registros de fallos de compilaciones de lanzamiento mediante la utilidad retrace. Sin un archivo de mapeo, un stack trace se convierte en un conjunto de letras a(), b(), c() sin posibilidad de restaurar el contexto original.

Fase de ProGuardPropósitoResultado
ShrinkAnálisis del grafo de llamadas y eliminación de código muertoMenos clases en APK
OptimizeInlining de métodos, eliminación de parámetros no utilizadosEjecución de código más rápida
ObfuscateRenombrado de clases, campos y métodosProtección contra ingeniería inversa
PreverifyAdición de atributos StackMap para JVMCompatibilidad con Java 6+

¿Qué es R8?

R8 es una herramienta de ofuscación y minificación de próxima generación de Google, presentada por primera vez en Android Studio 3.3 (noviembre de 2018) y convertida en estándar en AGP 3.4 (agosto de 2019). A diferencia de ProGuard, R8 forma parte del compilador D8/R8 que convierte bytecode Java en formato DEX. R8 realiza todas las fases — ofuscación, minificación y optimización — en un solo paso, sin pasar archivos intermedios entre herramientas.

Google desarrolló R8 con dos objetivos: acelerar las compilaciones (ProGuard funcionaba como herramienta externa) y proporcionar una integración perfecta con el stack moderno de Android (Desugar, Core Library Desugaring, D8). R8 está escrito en Kotlin y Java y forma parte del repositorio R8/Desugar en AOSP (Android Open Source Project).

Una ventaja importante de R8 es la compatibilidad total hacia atrás con las reglas de ProGuard. Los archivos .pro existentes funcionan sin cambios. R8 incluso admite directivas específicas de ProGuard, incluyendo -whyareyoukeeping, -printconfiguration y -printmapping. Esto significa que la transición de ProGuard a R8 es transparente: basta con actualizar AGP.

kotlin
// build.gradle.kts — activación de R8 mediante minifyEnabled
android {
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            isShrinkResources = true

            proguardFiles(
                // Configuración básica de Android SDK
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // Reglas personalizadas del proyecto
                "proguard-rules.pro"
            )
        }
    }
}

El código muestra una configuración estándar de compilación de lanzamiento. El indicador isMinifyEnabled = true activa R8 para ofuscación y optimización. isShrinkResources = true elimina adicionalmente los recursos no utilizados. getDefaultProguardFile carga las reglas predeterminadas del SDK, mientras que proguard-rules.pro contiene configuraciones específicas del proyecto.

Ofuscación de Código en Android

Ofuscación es el proceso de transformar el código fuente en una forma difícil de analizar para los humanos pero que conserva toda la funcionalidad. En el contexto de Android, la ofuscación significa renombrar clases, métodos y campos a nombres cortos y sin significado: com.example.app.auth.LoginManager se convierte en a.a.a, el método authenticateUser en a, el campo userToken en b.

Por Qué es Necesaria la Ofuscación

Los archivos APK de Android son archivos que se pueden abrir con cualquier archivador (ZIP, 7z, WinRAR). Sin ofuscación, un atacante obtiene un mapa completo de la aplicación: nombres de paquetes, clases, métodos y campos. Herramientas como jadx o Bytecode Viewer pueden restaurar código Java casi original a partir de archivos DEX en segundos. La ofuscación no hace que el código sea invulnerable, pero eleva significativamente la barrera de entrada: en lugar de nombres significativos, el lector ve a(), b(), c().

Objetivos típicos de la ofuscación: proteger la lógica comercial (algoritmos, fórmulas de cálculo), dificultar el robo de claves API y tokens, prevenir la sustitución de clases mediante reflection, y evitar la modificación y parcheo de APK (ataque de reempaquetado). En la práctica, el 70% de las tareas se resuelven solo con el renombrado — por eso se usan ProGuard/R8.

Ejemplo de Reglas ProGuard

A continuación se muestra un archivo típico proguard-rules.pro para un proyecto Android con Retrofit, Gson y Parcelable. Las reglas -keep conservan las clases y métodos necesarios para el funcionamiento de las bibliotecas mediante reflection. Sin estas reglas, R8 eliminará o renombrará las clases a las que la biblioteca accede por nombre de cadena.

pro
# =====================
# Retrofit — conservación de interfaces
# =====================
-keep,allowobfuscation,allowshrinking interface retrofit2.** { *; }
-keepattributes Signature, Exceptions

# =====================
# Gson — serialización 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 — eliminación de logs de la versión de lanzamiento
# =====================
-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(...);
}

# =====================
# Clases de datos Kotlin — conservación de constructores
# =====================
-keepclassmembers class * {
    @kotlin.Metadata <fields>;
}

# =====================
# Activity — punto de entrada
# =====================
-keep class * extends android.app.Activity {
    @android.annotation.SuppressLint <methods>;
}

Cada directiva en un archivo .pro resuelve una tarea específica. -keep evita que la clase completa sea eliminada o renombrada. -keepclassmembers solo protege los miembros de la clase (campos y métodos) pero permite eliminar la clase en sí si no se utiliza. -assumenosideeffects indica a R8 que una llamada a método no tiene efectos secundarios y puede eliminarse de forma segura. La directiva -keepattributes conserva metadatos en el bytecode — anotaciones, firmas, excepciones.

La regla -keep,allowobfuscation,allowshrinking para Retrofit permite a R8 renombrar las interfaces pero no eliminarlas. Esto es necesario porque Retrofit accede a las interfaces a través de proxies dinámicos (java.lang.reflect.Proxy), y su eliminación provocaría ClassNotFoundException en tiempo de ejecución. Del mismo modo, Gson utiliza reflection para acceder a campos anotados con @SerializedName — sin -keepclassmembers los campos se eliminarán como no utilizados.

Minificación y ShrinkResources

Minificación (shrinking) es el proceso de eliminar código y recursos no utilizados de la compilación final. ProGuard y R8 analizan el grafo de llamadas comenzando desde los puntos de entrada (Activity, Service, BroadcastReceiver) y eliminan las clases y métodos a los que no se puede llegar a través de la cadena de llamadas. ShrinkResources es una etapa adicional que elimina los recursos no utilizados de res/ (layout, drawable, string, color).

La minificación proporciona el mayor beneficio en proyectos grandes con bibliotecas. Un escenario típico: un proyecto utiliza el 10% del código de una biblioteca conectada (por ejemplo, Google Play Services). Sin minificación, todo el código de la biblioteca termina en el APK. Con minificación, R8 elimina el 70–90% del código de la biblioteca, dejando solo las clases y métodos realmente utilizados. Esto impacta directamente en el tamaño del APK, el tiempo de carga y el consumo de memoria.

ShrinkResources en Acción

El mecanismo ShrinkResources funciona junto con la minificación de código. Después de que R8 determina qué clases se utilizan, la reducción de recursos analiza las referencias a recursos desde el código: R.layout.main, R.drawable.icon, getString(R.string.title). Todos los recursos sin una referencia directa o indirecta se eliminan del APK o AAB final. Esto se hace utilizando el archivo de recursos resources.arsc y las carpetas res/.

Un detalle importante: los recursos pueden ser accedidos mediante getIdentifier() o Resources.getResourceName() por nombre de cadena, evitando la clase R. En tales casos, R8 no ve un enlace directo y puede eliminar un recurso que realmente se utiliza. Para proteger dichos recursos, existe la directiva -keep class **.R$* { *; } — conserva todos los identificadores de la clase R.

xml
<!-- Ejemplo: recurso usado solo mediante getIdentifier() -->
<string name="dynamic_title_welcome">Bienvenido</string>
<string name="dynamic_title_share">Compartir</string>

<!-- Código Kotlin que accede por cadena -->
<!-- val title = getString(resources.getIdentifier( -->
<!--     \"dynamic_title_${type}\", \"string\", packageName)) -->

En este caso, R8 no ve una referencia estática a dynamic_title_welcome en la clase R porque el acceso es a través de getIdentifier con un nombre dinámico. Para conservar dichos recursos, agregue la directiva -keepclassmembers class **.R$string { *; } a proguard-rules.pro — evita la eliminación de cualquier campo de todas las clases R$string.

DirectivaPropósitoEjemplo
-keepConserva la clase y todos sus miembros-keep class com.example.api.** { *; }
-keepclassmembersConserva solo los miembros de la clase-keepclassmembers class * { @SerializedName <fields>; }
-keepattributesConserva metadatos del bytecode-keepattributes *Annotation*, Signature
-assumenosideeffectsElimina llamadas sin efectos secundarios-assumenosideeffects class Log { d(...); }
-dontwarnSuprime advertencias-dontwarn com.example.legacy.**

R8 vs ProGuard: Diferencias Clave

A pesar de que R8 es el sucesor de ProGuard, existen diferencias fundamentales entre las herramientas en arquitectura, rendimiento y comportamiento. Google suspendió oficialmente el soporte de ProGuard en Android Gradle Plugin a partir de AGP 7.0, pero ProGuard sigue utilizándose en proyectos que requieren un comportamiento de optimización específico no disponible en R8.

Tabla Comparativa

CaracterísticaProGuardR8
DesarrolladorGuardSquare (Eric Lafarge)Google
Año de lanzamiento20022018 (estable en 2019)
Arquitectura4 fases separadas (shrink → optimize → obfuscate → preverify)Un solo paso: shrink + optimize + obfuscate simultáneamente
Integración en AGPHerramienta externa, ejecutada después de javacIntegrada en el compilador D8 DEX
Velocidad de compilación2–3 veces más lentoMás rápido gracias a un solo paso e integración nativa
Soporte de KotlinLimitado (problemas con inline, lambdas, coroutines)Completo: coroutines, funciones inline, data class
Archivo de mapeomapping.txt (compatible con retrace)mapping.txt (mismo formato)
Personalización de optimización60+ opciones -optimizationpasses, -optimizationsLimitada: la mayoría de optimizaciones activadas por defecto
Estado de soporteReemplazado por R8 (AGP 7.0+ no lo usa)Desarrollo activo, parte de AOSP

Cuando R8 Puede Romper la Compilación

R8 es más agresivo que ProGuard al eliminar código que considera muerto. Esto provoca situaciones donde la compilación de depuración funciona pero la de lanzamiento falla con ClassNotFoundException o NoSuchMethodException. Casos típicos: bibliotecas que usan reflection por nombre de clase (Gson, Moshi, Retrofit, Room, Dagger); llamadas a ServiceLoader o java.util.ServiceLoader; proxies dinámicos (java.lang.reflect.Proxy); métodos nativos (JNI). La solución es agregar -keep para todas las clases que se invocan mediante reflection.

pro
# Problemas típicos de reflection — R8 no ve el enlace estático

# Room — conservación de DAO y migraciones
-keep class * extends androidx.room.RoomDatabase { *; }
-keep class *.DatabaseMigrations { *; }

# Dagger / Hilt — conservación de componentes
-keep class * extends dagger.hilt.android.components.** { *; }

# JNI — no renombrar métodos nativos
-keepclasseswithmembernames class * {
    native <methods>;
}

# Data Binding — conservación de clases Binding
-keep class *.databinding.** { *; }

Si después de agregar reglas la compilación sigue fallando, use el indicador -printconfiguration full-config.txt en proguard-rules.pro. R8 generará un archivo de configuración completo que muestra qué reglas se aplican y qué clases se conservan. También es útil la directiva -whyareyoukeeping class com.example.MyClass — muestra la razón por la que R8 decidió conservar dicha clase.

Configuración de Reglas ProGuard

La configuración adecuada de las reglas de ProGuard es la clave para una ofuscación estable sin errores en tiempo de ejecución. A continuación se presenta un proceso de configuración paso a paso para un proyecto nuevo o para un proyecto donde la ofuscación causa errores.

Paso 1: Configuración Básica

Comience incluyendo el archivo estándar de Android SDK — proguard-android-optimize.txt. Contiene reglas para componentes básicos de Android: Activity, Service, BroadcastReceiver, ContentProvider, View, Fragment. Este archivo se encuentra en la carpeta del SDK: $ANDROID_HOME/tools/proguard/proguard-android-optimize.txt. Si usa AGP, getDefaultProguardFile lo cargará automáticamente.

Paso 2: Bibliotecas

Cada biblioteca popular tiene reglas ProGuard recomendadas. Retrofit, OkHttp, Glide, Fresco, Coil, Room, Dagger/Hilt, Kotlin Coroutines — todas requieren reglas -keep específicas. Normalmente las reglas están incluidas en la biblioteca AAR y se vinculan automáticamente mediante reglas de consumidor. Verifique que la biblioteca proporcione un archivo proguard.txt dentro del AAR — esto indica que las reglas ya están contempladas.

Paso 3: Pruebas de la Compilación de Lanzamiento

Antes de publicar, asegúrese de probar la compilación de lanzamiento en un dispositivo real o emulador. Los problemas de ofuscación solo se manifiestan en tiempo de ejecución. Verifique: autorización (inicio de sesión/registro), carga de datos desde la red, navegación entre pantallas, cámara y galería, notificaciones push, Deeplinks, WebView. Cada fallo en la compilación de lanzamiento debe decodificarse usando retrace con el archivo de mapeo, y deben agregarse las reglas -keep faltantes.

Paso 4: Archivo de Mapeo y CI

El archivo de mapeo se genera en build/outputs/mapping/release/mapping.txt. Este archivo debe conservarse: sin él es imposible decodificar registros de fallos de Google Play Console. Incluya mapping.txt en su sistema de control de versiones o súbalo a los artefactos de CI. Google Play Console acepta el archivo de mapeo automáticamente al subir un AAB con uploading mapping.txt habilitado.

A continuación se muestra un flujo de trabajo completo de configuración de ofuscación en el archivo proguard-rules.pro con comentarios para cada grupo de reglas.

pro
# ===========================================
# proguard-rules.pro — ejemplo completo
# ===========================================

# --- Configuración general ---
-keepattributes *Annotation*, Signature, Exceptions, InnerClasses, EnclosingMethod
-dontpreverify

# --- Componentes 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 {}

# --- Serialización ---
-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();
}

# --- Solo R8: conservación forzada ---
# (ProGuard ignora esta directiva)
-keep,allowobfuscation class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator CREATOR;
}

Después de la configuración, ejecute la compilación: ./gradlew assembleRelease. Verifique que aparecieron archivos en build/outputs/mapping/release/: mapping.txt (mapeo de nombres originales a ofuscados), seeds.txt (clases conservadas por reglas -keep), usage.txt (clases eliminadas durante la minificación). El tamaño del APK después de la ofuscación debería disminuir entre un 20 y un 50% dependiendo del número de bibliotecas conectadas.

Preguntas Frecuentes

¿En qué se diferencia R8 de ProGuard?

R8 es el sucesor de ProGuard, desarrollado por Google. R8 realiza ofuscación, minificación y optimización en un solo paso, funciona 2–3 veces más rápido que ProGuard y está integrado directamente en Android Gradle Plugin. ProGuard utiliza cuatro fases separadas y requiere ejecución externa. A partir de AGP 7.0, ProGuard no se utiliza — R8 funciona por defecto.

¿Necesito escribir reglas ProGuard al usar R8?

Sí, R8 utiliza las mismas reglas ProGuard (archivos .pro). Las directivas -keep, -keepclassmembers, -keepattributes, -assumenosideeffects funcionan de manera idéntica. Las reglas básicas vienen en proguard-android-optimize.txt del Android SDK, mientras que las reglas específicas de bibliotecas (Retrofit, Room, Gson) se agregan en proguard-rules.pro del proyecto. Sin estas reglas, R8 puede eliminar clases necesarias para bibliotecas que funcionan mediante reflection.

¿Cómo habilitar R8 en un proyecto Android?

R8 está habilitado por defecto en Android Gradle Plugin a partir de AGP 3.4. Para activar la minificación, establezca isMinifyEnabled = true en el bloque release buildType de build.gradle.kts. El indicador adicional isShrinkResources = true habilita la eliminación de recursos no utilizados. En gradle.properties, puede deshabilitar R8 forzosamente mediante android.enableR8=false, pero no se recomienda — R8 es más rápido y estable.

¿Qué es la ofuscación de código en Android?

Ofuscación — renombrado de clases, métodos y campos a nombres cortos sin significado (a, b, c). La clase com.example.app.auth.LoginManager se convierte en a.a.a, el método authenticateUser en a. Esto dificulta la ingeniería inversa de la aplicación pero no afecta la lógica de ejecución. ProGuard y R8 solo renombran los elementos no protegidos por reglas -keep. El archivo de mapeo conserva la correspondencia de nombres originales y ofuscados para decodificar registros de fallos.

¿Cómo depurar un registro de fallos de una aplicación ofuscada?

Para decodificar un stack trace, use la utilidad retrace (parte del SDK de ProGuard/R8). Comando: retrace mapping.txt crash-stacktrace.txt. El archivo de mapeo se encuentra en build/outputs/mapping/release/mapping.txt. Google Play Console también admite la carga de mapping.txt al publicar un AAB — los registros de fallos se decodifican automáticamente en la consola. Sin un archivo de mapeo, el stack trace solo contendrá nombres ofuscados como a.b.c(), lo que es inútil para la depuración.

Resumen

  • ProGuard — una herramienta clásica de ofuscación y optimización de bytecode Java, que consta de cuatro fases secuenciales
  • R8 — un sucesor moderno de Google, integrado en AGP, que realiza todas las fases en un solo paso con un rendimiento 2–3 veces superior
  • Ofuscación renombra clases, métodos y campos a nombres cortos, dificultando la ingeniería inversa y protegiendo la lógica comercial de la aplicación
  • Minificación elimina código y recursos no utilizados, reduciendo el tamaño del APK entre un 20 y un 50% en proyectos típicos
  • Reglas de ProGuard (archivos .pro) controlan el comportamiento de ofuscación — las directivas -keep, -keepclassmembers, -assumenosideeffects especifican qué elementos se conservan, eliminan o renombran
  • Archivo de mapeo (mapping.txt) es un artefacto de compilación críticamente importante para decodificar registros de fallos de compilaciones de lanzamiento mediante retrace
  • Pruebas de la compilación de lanzamiento en un dispositivo real son obligatorias — los problemas de ofuscación solo se manifiestan en tiempo de ejecución y requieren agregar reglas -keep faltantes

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también