Optimizando tu aplicación Android

Usando ProGuard/R8 y otros mecanismos para disminuir el tamaño e incrementar la velocidad de ejecución de tu aplicación Android

Carlos Fernando Arboleda Garcés

https://linkedin.com/in/carboleda/

  • Los recursos de los dispositivos móviles son limitados

  • Nuestra app compite contra otras cientos de apps

  • Cada vez usamos mas librerías de terceros

  • La gama de dispositivos en Android es muy grande

¿Por qué es necesaria la optimización?

Con Android App Bundle se descarga necesario para la aplicación funcione solo en ese dispositivo.

 

  • Administrado por Google

  • Personalizar las funcionalidades entregadas por País o tipo  de dispositivo

  • Instalación de módulos por demanda

Android App Bundle

Android App Bundle

Samsung Android 8.1

Xiaomi Android 9

¿Qué necesito utilizar Android App Bundle?

  1. Ser propietario de un cuenta en Google Play Store
  2. Exportar la clave privada en Android Studio y subirla Google Play Store
  3. Generar Bundle de la aplicación y publicarlo
  4. Google se encarga del resto...

ProGuard y R8

ProGuard es una mítica herramienta que reduce el código, detectando y eliminando las clases, campos, métodos, paquetes y atributos no utilizados (incluidas las librerías de tercero).

R8 es básicamente la evolución de ProGuard, fue anunciado a finales del 2018

ProGuard y R8

DEX

Jack & Jill

D8

R8

ProGuard y R8

Con ProGuard y R8 se reduce el tamaño del bytecode aproximadamente un 20 o 50% y mejora el rendimiento un 20%.

En qué consiste la optimización de ProGuard?

  • Diminuye el tamaño del APK.
  • Disminuye el consumo de memoria en la ejecución.
  • Puede aumentar la velocidad de ejecución.
  • Dificulta la interpretación del código fuente en caso de una descompilación.
  1. Reducir código: Eliminar paquetes, clases, métodos y campos no utilizados
  2. Optimizar código: Hacer el código mas pequeño y eficiente a nivel instrucciones
  3. Ofuscación de nombres: Renombrar los paquetes, clases, métodos y campos restantes
android {
    ...
    buildTypes {
        debug {
            minifyEnabled true
            useProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        release {
            minifyEnabled true//Solo con este flag activamos ProGuard
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
//App build.gradle

Activar ProGuard

...
android.enableR8=true

//Project gradle.properties

Activar R8

¿Cómo configurar ProGuard y R8?

Cómo funciona la configuración?

  • minifyEnabled: Determina si se elimina el código no utilizado. Valor por defecto false.
  • useProguard: Determina si se ofusca el código, es decir si cambia los nombres de paquetes, clases, métodos y campos. Valor defecto true.
  • proguardFiles: Permite configurar las reglas de conservación de código.

Errores comunes

Ciclo de compilación ProGuard

Con cada compilación, ProGuard crea los siguientes archivos:

  • dump.txt Describe la estructura interna de todos los archivos de clase del APK.
  • mapping.txt Proporciona una traducción entre la clase original y la oculta, el método y los nombres de campos.
  • seeds.txt Indica las clases y los miembros que no se ocultaron.
  • usage.txt Indica el código que se quitó del APK.

 

Estos archivos se guardan en:

<module-name>/build/outputs/mapping/release/

######## CUSTOM RULES FOR MY CODE ########
# Application classes that will be serialized/deserialized over Gson
-keep class co.devhack.musicapp.domain.model.** { <fields>; }
######## CUSTOM RULES FOR MY CODE ########


######## RETROFIT ########
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations

# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @retrofit2.http.* <methods>;
}

# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**

# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit

# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions
######## RETROFIT ########

# proguad-rules.pro

Configurar reglas de ProGuard

Analizando APK - ANTES

Analizando APK - DESPUÉS

Diferencia de tamaño tras la optimización

minifyEnabled false
useProguard false
minifyEnabled true
useProguard false
minifyEnabled true
useProguard true

Comparando APKs en Android Studio

APK 40% más pequeño

private void calculatePayroll(specialList, employeeGroup) {
    Employee employee;
    while(employeeGroup.hasMore()) {
        employee = employeeGroup.getNext(true);
        employee.updateSalary();
        distributeCheck(employee);
    }
}
private void a(a, b) {
    C c;
    while(b.a()) {
        c = b.ab(true);
        c.a();
        d(c);
    }
}

Código original

Código ofuscado

Osfucación de código

Consideraciones

  1. En caso de ser necesario, agregar al archivo proguard-rules.pro las reglas de cada dependencia que use el proyecto. https://github.com/krschultz/android-proguard-snippets/tree/master/libraries
  2. El análisis de código realizado por ProGuard es estático, lo cual no es compatible con el uso de Java Reflection
  3. Si el proyecto utiliza Android Gradle plugin 3.4.0 o superior al poner minifyEnable en true por defecto se usará R8
  4. Las dependencias Support Library vienen configuradas por defecto reglas de ProGuard por lo que no es necesario crearlas https://stackoverflow.com/a/41580019/3167601
  5. Cómo descompilar APK? https://stackoverflow.com/a/4177581/3167601

Herramientas

Enlaces de interes

Made with Slides.com