Elige las bibliotecas con cuidado

Para habilitar la optimización de apps, debes usar bibliotecas que sean compatibles con la optimización de Android. Si una biblioteca no está configurada para la optimización de Android (por ejemplo, si usa la reflexión sin empaquetar reglas de conservación asociadas), es posible que no sea adecuada para una app para Android. En esta página, se explica por qué algunas bibliotecas son más adecuadas para la optimización de apps y se proporcionan sugerencias generales para ayudarte a elegir.

Sugerencias generales para elegir bibliotecas

Usa estas sugerencias para asegurarte de que tus bibliotecas sean compatibles con la optimización de la app.

Prefiere la generación de código antes que la reflexión

Elige bibliotecas que usen la generación de código (codegen) en lugar de la reflexión. Con la generación de código, el optimizador puede determinar qué código se usa realmente en el entorno de ejecución y qué código se puede quitar. Puede ser difícil saber si una biblioteca usa codegen o reflexión, pero hay algunos indicios. Consulta las sugerencias para obtener ayuda.

Para obtener más información sobre la generación de código en comparación con la reflexión, consulta Optimización para autores de bibliotecas.

Verifica el uso de la reflexión (avanzado)

Para saber si una biblioteca usa la reflexión, inspecciona su código. Si la biblioteca usa la reflexión, verifica que proporcione reglas de conservación asociadas. Es probable que una biblioteca use la reflexión si hace lo siguiente:

  • Usa clases o métodos de los paquetes kotlin.reflect o java.lang.reflect.
  • Usa las funciones Class.forName o classLoader.getClass.
  • Lee anotaciones en el entorno de ejecución, por ejemplo, si almacena un valor de anotación con val value = myClass.getAnnotation() o val value = myMethod.getAnnotation() y, luego, hace algo con value.
  • Llama a métodos con el nombre del método como una cadena, como en el siguiente ejemplo:

    // Calls the private `processData` API with reflection
    myObject.javaClass.getMethod("processData", DataType::class.java)
    ?.invoke(myObject, data)
    

Verifica si hay problemas de optimización

Cuando consideres una biblioteca nueva, consulta la herramienta de seguimiento de errores y los debates en línea de la biblioteca para verificar si hay problemas relacionados con la reducción o la configuración de la optimización de la app. Si es así, deberías buscar alternativas para esa biblioteca. Ten en cuenta lo siguiente:

  • Las bibliotecas de AndroidX y las bibliotecas como Hilt funcionan bien con la optimización de apps porque, en su mayoría, usan codegen en lugar de reflexión. Cuando usan la reflexión, proporcionan reglas de conservación mínimas para conservar solo el código necesario.
  • Las bibliotecas de serialización suelen usar la reflexión para evitar el código estándar cuando se crean instancias de objetos o se serializan. En lugar de enfoques basados en la reflexión (como Gson para JSON), busca bibliotecas que usen codegen para evitar estos problemas, por ejemplo, con la serialización de Kotlin o Moshi con codegen.
  • Si es posible, evita las bibliotecas que incluyan reglas de conservación para todo el paquete. Las reglas de conservación para todo el paquete pueden ayudar a resolver errores, pero las reglas de conservación amplias deben perfeccionarse con el tiempo para conservar solo el código necesario. Para obtener más información, consulta Adopta optimizaciones de forma incremental.
  • Antes de publicar una app que use una biblioteca de terceros, utiliza el Analizador de configuración de R8 para auditar las reglas de conservación que proporciona. Si revisas el informe, puedes verificar si las reglas de conservación de la biblioteca son demasiado amplias, lo que impide que R8 realice optimizaciones fundamentales en tu base de código. Esta verificación garantiza que las bibliotecas que selecciones se alineen con los objetivos de rendimiento de tu app y no introduzcan una sobrecarga de configuración innecesaria.
  • Las bibliotecas no deberían requerir que copies y pegues reglas de conservación de la documentación en un archivo de tu proyecto, en especial, reglas de conservación para todo el paquete. Estas reglas se convierten en una carga de mantenimiento para el desarrollador de apps a largo plazo, y son difíciles de optimizar y cambiar con el tiempo.

Habilita la optimización después de agregar una biblioteca nueva

Cuando agregues una biblioteca nueva, habilita la optimización después y verifica si hay errores. Si hay errores, busca alternativas para esa biblioteca o escribe reglas de conservación. Si una biblioteca no es compatible con la optimización, informa un error con esa biblioteca.

Cómo filtrar las reglas de conservación incorrectas (avanzado)

Las reglas de conservación son acumulativas. Esto significa que no se pueden quitar ciertas reglas que incluye una dependencia de biblioteca y que podrían afectar la compilación de otras partes de tu app. Por ejemplo, si una biblioteca incluye una regla para inhabilitar las optimizaciones de código, esa regla inhabilitará las optimizaciones de todo tu proyecto.

Debes evitar las bibliotecas con reglas de conservación que retengan código que realmente debería quitarse. Sin embargo, si debes usarlas, puedes filtrar las reglas como se muestra en el siguiente código:

// If you're using AGP 8.4 and higher
buildTypes {
    release {
        optimization.keepRules {
          it.ignoreFrom("com.somelibrary:somelibrary")
        }
    }
}

// If you're using AGP 7.3-8.3
buildTypes {
    release {
        optimization.keepRules {
          it.ignoreExternalDependencies("com.somelibrary:somelibrary")
        }
    }
}

Flujo de trabajo recomendado para reglas de biblioteca amplias

Si el analizador de configuración de R8 revela que una biblioteca incluye reglas de conservación demasiado amplias, filtra esas reglas solo en las compilaciones locales, no en las de producción. Para probar cuánto afectan las reglas a tu app, sigue estos pasos:

  1. Actualiza la biblioteca: Asegúrate de usar la versión más reciente de la biblioteca que marcó el Analizador de configuración de R8, ya que las versiones más recientes podrían incluir reglas de conservación de consumidores más estrictas. Para encontrar la versión más reciente, consulta la documentación de la biblioteca o su repositorio de Maven. Android Studio también marca las versiones más recientes de muchas dependencias. Después de la actualización, vuelve a ejecutar el analizador de configuración de R8 para verificar si se mantienen las reglas de conservación amplias.
  2. Verifica la mejora potencial de forma local: Copia las reglas de consumidor empaquetadas de la biblioteca en tu archivo de reglas de conservación local, filtra las reglas empaquetadas como se muestra en Cómo filtrar las reglas de conservación incorrectas y restringe las reglas copiadas. Ejecuta el Analizador de configuración de R8 antes y después de restringir las reglas para comparar las puntuaciones de reducción, optimización y ofuscación de tu app.
  3. Informa los hallazgos: Envía un informe de errores al encargado de mantenimiento de la biblioteca con los datos empíricos de tu informe del Analizador de configuración de R8 (por ejemplo, demostrando la cantidad específica de clases o métodos sin referencia que conserva su regla de comodín).

Caso de éxito: Por qué Gson falla con las optimizaciones

Gson es una biblioteca de serialización que suele causar problemas con la optimización de la app porque usa mucho la reflexión. En el siguiente fragmento de código, se muestra cómo se suele usar Gson, lo que puede causar fallas en el entorno de ejecución. Observa que, cuando usas Gson para obtener una lista de objetos User, no llamas al constructor ni pasas una fábrica a la función fromJson(). La construcción o el consumo de clases definidas por la app sin ninguno de los siguientes elementos es un indicio de que una biblioteca podría estar usando la reflexión abierta:

  • Clase de app que implementa una biblioteca, o una interfaz o clase estándar
  • Complemento de generación de código, como KSP
class User(val name: String)
class UserList(val users: List<User>)

// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()

Para comprender cómo funciona R8 en Gson, consulta las reglas de consumidor de Gson. Cuando R8 analiza este código y no ve que se cree una instancia de UserList ni de User en ningún lugar, puede cambiar el nombre de los campos o quitar los constructores que no parecen usarse, lo que provoca que la app falle. Si usas otras bibliotecas de manera similar, debes verificar que no interfieran en la optimización de la app y, si lo hacen, evitarlas.

Usa @SerializedName con versiones posteriores de Gson

Para definir tus modelos de datos de una manera compatible con las reglas de consumidor de Gson, anota tus campos con @SerializedName, como se muestra en el siguiente fragmento:

import com.google.gson.annotations.SerializedName

class User(@SerializedName("name") val name: String)
class UserList(@SerializedName("users") val users: List<User>)

Si usas la anotación @SerializedName, permites que R8 haga coincidir tus clases de modelo con las reglas de conservación incluidas en Gson versión 2.11.0 y posteriores. R8 conserva automáticamente los campos anotados y los constructores necesarios, lo que te permite confiar en las reglas empaquetadas de la biblioteca sin tener que mantener configuraciones manuales de ProGuard en tu proyecto.

Ten en cuenta que Room, Hilt y Moshi con codegen construyen tipos definidos por la app, pero usan codegen para evitar la necesidad de reflexión.