Elige las bibliotecas con cuidado

Para habilitar la optimización de apps, 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 agrupar las reglas de retenció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.

Prefiere codegen en lugar de reflexión

Por lo general, debes elegir bibliotecas que usen generación de código (codegen) en lugar de reflexión. Con codegen, el optimizador puede determinar con mayor facilidad qué código se usa realmente durante 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 indicadores. 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.

Sugerencias generales para elegir bibliotecas

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

Verifica si hay problemas de optimización

Cuando consideres una biblioteca nueva, revisa el seguimiento de errores y las discusiones 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 a esa biblioteca. Ten en cuenta lo siguiente:

  • Las bibliotecas de AndroidX y bibliotecas como Hilt funcionan bien con la optimización de apps porque usan codegen en lugar de reflexión. Cuando usan la reflexión, proporcionan reglas de retenció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 de plantilla 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 serialización de Kotlin.
  • Si es posible, se deben evitar las bibliotecas que incluyen reglas de retención para todo el paquete. Las reglas de retención para todo el paquete pueden ayudar a resolver errores, pero las reglas de retención amplias deben definirse mejor para conservar solo el código que se necesita. Para obtener más información, consulta Adopta optimizaciones de forma incremental.

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

Cuando agregues una biblioteca nueva, habilita la optimización después y comprueba 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.

Las reglas son acumulativas

Ten en cuenta que las reglas de retención son acumulativas. Esto significa que ciertas reglas que incluye una dependencia de biblioteca no se pueden quitar 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, esa regla inhabilitará las optimizaciones de todo tu proyecto.

Comprueba el uso de la reflexión (avanzado)

Es posible que puedas saber si una biblioteca usa reflexión a partir de la inspección de su código. Si la biblioteca usa reflexión, verifica que proporcione reglas de retención asociadas. Es probable que una biblioteca use 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 tiempo 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, por ejemplo:

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

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

Debes evitar las bibliotecas con reglas de retención que conservan 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 éxito: Por qué Gson falla 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, 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 signo de que una biblioteca podría estar usando reflexión abierta:

  • Clase de app que implementa una biblioteca, una interfaz o una clase estándar
  • Un 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()

Cuando R8 analiza este código y no ve la 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 causa que la app falle. Si usas otras bibliotecas de formas similares, debes verificar que no interfieran con la optimización de la app y, si lo hacen, evítalas.

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