Para habilitar la optimización de la app, debes usar bibliotecas compatibles con la optimización de Android. Si una biblioteca no está configurada para la optimización de Android (por ejemplo, si usa reflexión sin incluir 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
Sigue estas sugerencias para asegurarte de que tus bibliotecas sean compatibles con la optimización de la app.
Prefiere la generación de código a la reflexión
Elige bibliotecas que usen generación de código (codegen) en lugar de reflexión. Con la generación de código, el optimizador puede determinar con mayor facilidad qué código se usa realmente en el tiempo 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 Optimization for library authors.
Comprueba el uso de la reflexión (avanzado)
Puedes saber si una biblioteca usa la reflexión inspeccionando 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.reflectojava.lang.reflect. - Usa las funciones
Class.forNameoclassLoader.getClass. - Lee las anotaciones en el tiempo de ejecución, por ejemplo, si almacena un valor de anotación con
val value = myClass.getAnnotation()oval value = myMethod.getAnnotation()y, luego, hace algo convalue. Llama a métodos usando 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 usar una biblioteca nueva, revisa el registro de problemas y los debates en línea para verificar si hay problemas relacionados con la minimización o la configuración de la optimización de la app. Si es así, deberías buscar alternativas a esa biblioteca. Ten en cuenta lo siguiente:
- Las bibliotecas de AndroidX y las bibliotecas como Hilt funcionan bien con la optimización de la app 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 instancian o serializan objetos. En lugar de enfoques basados en la reflexión (como Gson para JSON), busca bibliotecas que usen codegen para evitar estos problemas, por ejemplo, usando Kotlin Serialization o Moshi con codegen.
- Si es posible, evita las bibliotecas que incluyen 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 generales se deben refinar con el tiempo para conservar solo el código necesario. Para obtener más información, consulta Adopta optimizaciones de forma incremental.
- Las bibliotecas no deberían requerir que copies y pegues reglas de conservación de la documentación en un archivo de tu proyecto, especialmente las reglas de conservación a nivel del paquete. A largo plazo, estas reglas se convierten en una carga de mantenimiento para el desarrollador de la app 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 a esa biblioteca o escribe reglas de conservación. Si una biblioteca no es compatible con la optimización, informa un error sobre ella.
Cómo filtrar las reglas de conservación incorrectas (avanzado)
Las reglas de conservación son acumulativas. Esto significa que ciertas reglas que incluye una dependencia de biblioteca no se pueden quitar y 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")
}
}
}
Caso de estudio: Por qué Gson se interrumpe 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 usa Gson de forma habitual, lo que puede provocar fallas fácilmente durante el tiempo de ejecución. Ten en cuenta 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(). Construir o consumir 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 la app que implementa una biblioteca, una interfaz estándar o una clase
- 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 haya creado una instancia de UserList o 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.
Para definir las clases de una manera compatible con las reglas de consumidor de Gson, usa el siguiente fragmento como referencia:
class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)
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.