Para habilitar la optimización de la app, 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 agrupar las 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 apps.
Prefiere la generación de código a la reflexión
Elige bibliotecas que usen la generación de código (codegen) en lugar de la reflexión. Con codegen, el optimizador puede determinar 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 signos. Consulta las sugerencias para obtener ayuda.
Para obtener más información sobre codegen en comparación con la reflexión, consulta Optimización para autores de bibliotecas.
Verifica 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 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 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 el registro 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 los hay, debes intentar 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 apps porque usan principalmente codegen en lugar de reflexión. Cuando usan la reflexión, proporcionan reglas de conservación mínimas para conservar solo el código que se necesita.
- Las bibliotecas de serialización suelen usar la reflexión para evitar el código estándar cuando se crean instancias o se 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 {:.external} o Moshi con codegen.
- Si es posible, evita las bibliotecas que incluyen reglas de conservación en todo el paquete. Las reglas de conservación en todo el paquete pueden ayudar a resolver errores, pero las reglas de conservación amplias deben refinarse para conservar solo el código que se necesita. Para obtener más información, consulta Adopta optimizaciones de forma incremental.
- Antes de publicar una app que use una biblioteca de terceros, usa el Analizador de configuración de R8 para auditar sus reglas de conservación proporcionadas. 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 críticas 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 expansión 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, no las reglas de conservación en 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 a esa biblioteca o escribe reglas de conservación. Si una biblioteca no es compatible con la optimización, informa un error con esa biblioteca.
Filtra las reglas de conservación incorrectas (avanzado)
Las reglas de conservación son acumulativas. Esto significa que no se podrán quitar las reglas que incluyan una dependencia de la biblioteca y es posible que afecten la compilación de otras partes de tu app. Por ejemplo, si una biblioteca incluye una regla para inhabilitar las optimizaciones de código, esta inhabilitará las optimizaciones de todo tu proyecto.
Debes evitar las bibliotecas con reglas de conservación que retengan código que realmente se deba quitar. 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 apps porque usa mucho la reflexión. En el siguiente fragmento de código, se muestra cómo se usa Gson normalmente, lo que puede provocar fallas en 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(). Crear o consumir clases definidas por la app sin ninguna de las siguientes opciones es un signo de que una biblioteca podría usar 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 UserList o User con instancias
en ningún lugar, puede cambiar el nombre de los campos o quitar los constructores que no parecen estar
en uso, lo que provoca que tu app falle. Si usas otras bibliotecas de manera similar, debes verificar que no interfieran con la optimización de la app y, si lo hacen, evítalas.
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.