El sistema de compilación de Android 12 incluye cambios en la compilación AOT
de archivos DEX (dexpreopt) para módulos de Java que tienen <uses-library>
las dependencias. En algunos casos, estos cambios
del sistema de compilación pueden interrumpir
compilaciones. Usa esta página para prepararte ante las fallas y sigue las recetas que se indican en esta página para solucionarlas y mitigarlas.
Dexpreopt es el proceso de compilación anticipada de bibliotecas Java y de Google Chat. Dexpreopt se realiza en el host en el momento de la compilación (a diferencia de dexopt, que se realiza en el dispositivo). Estructura de dependencias de bibliotecas compartidas usadas por un Java módulo (una biblioteca o una app) se conoce como su contexto de cargador de clases (CLC). Para garantizar la exactitud de dexpreopt, los CLCs del tiempo de compilación y del tiempo de ejecución deben coincidir. El CLC en el tiempo de compilación es lo que usa el compilador dex2oat en el momento de dexpreopt. (se registra en los archivos ODEX) y el CLC de tiempo de ejecución es el contexto en el que código compilado previamente se carga en el dispositivo.
Estos CLC de tiempo de compilación y tiempo de ejecución deben coincidir por motivos de exactitud y rendimiento. Para mayor precisión, es necesario manejar clases duplicadas. Si las dependencias de la biblioteca compartida en el tiempo de ejecución son diferentes de las que se usan para la compilación, es posible que algunas de las clases se resuelvan de manera diferente, lo que causará errores sutiles en el tiempo de ejecución. El rendimiento también se ve afectado por las verificaciones .
Casos de uso afectados
El primer inicio es el caso de uso principal que se ve afectado por estos cambios: si ART detecta una discrepancia entre los CLC del tiempo de compilación y del tiempo de ejecución, rechaza los artefactos de dexpreopt y, en su lugar, ejecuta dexopt. Para los inicios posteriores, esto es correcto las apps se pueden dexoptar en segundo plano y almacenarse en el disco.
Áreas afectadas de Android
Esto afecta a todas las apps y bibliotecas de Java que tienen dependencias de entorno de ejecución en otras bibliotecas de Java. Android tiene miles de apps, y cientos de ellas usan las bibliotecas compartidas. Los socios también se ven afectados, ya que tienen sus propias bibliotecas y apps.
Cómo dividir cambios
El sistema de compilación debe conocer las dependencias <uses-library>
antes
genera reglas de compilación de dexpreopt. Sin embargo, no puede acceder al manifiesto directamente ni leer las etiquetas <uses-library>
que contiene, ya que el sistema de compilación no tiene permiso para leer archivos arbitrarios cuando genera reglas de compilación (por motivos de rendimiento). Además, el manifiesto puede empaquetarse dentro de un APK o una compilación previa. Por lo tanto, el objeto <uses-library>
La información debe estar presente en los archivos de compilación (Android.bp
o Android.mk
).
Anteriormente, ART usaba una solución que ignoraba las dependencias de bibliotecas compartidas (conocidas como &-classpath
). Esto no era seguro y causaba errores sutiles, por lo que se quitó la solución en Android 12.
Como resultado, los módulos de Java que no proporcionan <uses-library>
correctos
información en sus archivos de compilación pueden causar fallas de compilación (causadas por una
discrepancia de CLC en el tiempo de compilación) o regresiones de tiempo de primer inicio (causadas por un tiempo de inicio
discrepancia de CLC seguida de dexopt).
Ruta de migración
Sigue estos pasos para corregir una compilación dañada:
Configura para inhabilitar globalmente la verificación del tiempo de compilación para un producto en particular
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
en el archivo makefile del producto. Esto corrige errores de compilación (excepto en casos especiales, en la sección Cómo corregir fallas). Sin embargo, Esta es una solución temporal y puede causar una discrepancia de CLC en el inicio y luego el dexopt.
Se corrigieron los módulos que fallaron antes de inhabilitar globalmente la verificación de tiempo de compilación Para ello, agrega la información necesaria de
<uses-library>
a sus archivos de compilación (consulta Cómo corregir fallas para obtener más detalles). Para la mayoría de los módulos, esto requiere agregar algunas líneas enAndroid.bp
, o enAndroid.mk
Inhabilita la verificación del tiempo de compilación y dexpreopt para los casos problemáticos, por módulo. Inhabilita dexpreopt para no desperdiciar tiempo de compilación ni almacenamiento en artefactos que se rechazan durante el inicio.
Para volver a habilitar de forma global la verificación del tiempo de compilación, anula la configuración de
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES
que se estableció en el paso 1. La compilación no debería fallar después de este cambio (debido a los pasos 2 y 3).Corrige los módulos que inhabilitaste en el paso 3, uno a la vez, y, luego, vuelve a habilitar dexpreopt y la verificación
<uses-library>
. Informa los errores si es necesario.
Las verificaciones de <uses-library>
en el tiempo de compilación se aplican en Android 12.
Corregir fallas
En las siguientes secciones, se explica cómo corregir tipos específicos de fallas.
Error de compilación: Discrepancia de CLC
El sistema de compilación verifica la coherencia de tiempo de compilación entre la información de
Archivos Android.bp
o Android.mk
y el manifiesto El sistema de compilación no puede leer
el manifiesto, pero puede generar reglas de compilación para leerlo (extraer
desde un APK, si es necesario) y comparar las etiquetas <uses-library>
en el manifiesto
en la información de <uses-library>
en los archivos de compilación. Si la verificación falla,
el error se verá de la siguiente manera:
error: mismatch in the <uses-library> tags between the build system and the manifest:
- required libraries in build system: []
vs. in the manifest: [org.apache.http.legacy]
- optional libraries in build system: []
vs. in the manifest: [com.x.y.z]
- tags in the manifest (.../X_intermediates/manifest/AndroidManifest.xml):
<uses-library android:name="com.x.y.z"/>
<uses-library android:name="org.apache.http.legacy"/>
note: the following options are available:
- to temporarily disable the check on command line, rebuild with RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" and disable AOT-compilation in dexpreopt)
- to temporarily disable the check for the whole product, set PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles
- to fix the check, make build system properties coherent with the manifest
- see build/make/Changes.md for details
Como sugiere el mensaje de error, hay varias soluciones, dependiendo del urgencia:
- Para obtener una corrección temporal para todo el producto, establece
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
en el archivo makefile del producto. La verificación de coherencia del tiempo de compilación aún se realiza, pero una falla de verificación no significa que haya una falla de compilación. En cambio, una falla de verificación hace que el sistema de compilación cambie a una versión inferior. Filtro del compilador dex2oat averify
en dexpreopt, que inhabilita la compilación AOT completamente para este módulo. - Para obtener una solución rápida y global de la línea de comandos, usa la variable de entorno
RELAX_USES_LIBRARY_CHECK=true
. Tiene el mismo efecto quePRODUCT_BROKEN_VERIFY_USES_LIBRARIES
, pero está diseñado para usarse en la línea de comandos. El de entorno anula la variable de producto. - Para obtener una solución que solucione la causa raíz del error, haz que el sistema de compilación conozca las etiquetas
<uses-library>
en el manifiesto. Una inspección del mensaje de error muestra las bibliotecas que causan el problema (comoAndroidManifest.xml
o el manifiesto dentro de un APK que se puede verificar con `aapt dump badging $APK | grep uses-library
`).
Para módulos Android.bp
:
Busca la biblioteca que falta en la propiedad
libs
del módulo. Si es ahí, Soong normalmente agrega esas bibliotecas automáticamente, excepto que en estas casos especiales:- La biblioteca no es una biblioteca de SDK (se define como
java_library
en lugar de quejava_sdk_library
). - La biblioteca tiene un nombre diferente (en el manifiesto) de su módulo. nombre (en el sistema de compilación).
Para solucionar este problema de forma temporal, agrega
provides_uses_lib: "<library-name>"
en la definición de la bibliotecaAndroid.bp
. Para una solución a largo plazo, corrige el problema Problema: convierte la biblioteca en una biblioteca de SDK o cambia el nombre de su módulo.- La biblioteca no es una biblioteca de SDK (se define como
Si el paso anterior no te brindó una solución, agrega
uses_libs: ["<library-module-name>"]
para las bibliotecas requeridas ooptional_uses_libs: ["<library-module-name>"]
para que las bibliotecas opcionales la definición del móduloAndroid.bp
. Estas propiedades aceptan una lista de los nombres de los módulos. El orden relativo de las bibliotecas de la lista debe ser el mismo como el orden en el manifiesto.
Para módulos Android.mk
:
Verifica si la biblioteca tiene un nombre diferente (en el manifiesto) del nombre del módulo (en el sistema de compilación). Si es así, soluciona el problema temporalmente agregando
LOCAL_PROVIDES_USES_LIBRARY := <library-name>
en el archivoAndroid.mk
de la biblioteca o agregaprovides_uses_lib: "<library-name>"
enAndroid.bp
archivo de la biblioteca (ambos casos son posibles, ya que se necesita un móduloAndroid.mk
) podría depender de una bibliotecaAndroid.bp
). Para obtener una solución a largo plazo, corrige el problema subyacente: cambiar el nombre del módulo de la biblioteca.Agregar
LOCAL_USES_LIBRARIES := <library-module-name>
para obligatorio bibliotecas; agregarLOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name>
para bibliotecas opcionales a la definiciónAndroid.mk
del módulo. Estos aceptan una lista de nombres de módulos. El orden relativo de las bibliotecas en la lista debe ser el mismo que en el manifiesto.
Error de compilación: ruta de acceso a la biblioteca desconocida
Si el sistema de compilación no puede encontrar una ruta de acceso a un jar DEX de <uses-library>
(ya sea una ruta de acceso en el tiempo de compilación en el host o una ruta de acceso de instalación en el dispositivo), la compilación suele fallar. Si no se encuentra una ruta, es posible que la biblioteca esté configurada de una manera inesperada. Inhabilita dexpreopt para corregir la compilación de forma temporal para el problema
módulo.
Android.bp (propiedades del módulo):
enforce_uses_libs: false,
dex_preopt: {
enabled: false,
},
Android.mk (variables de módulo):
LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false
Envía un informe de errores para investigar las situaciones que no se admiten.
Error de compilación: falta la dependencia de la biblioteca
Un intento de agregar <uses-library>
X del manifiesto del módulo Y al archivo de compilación de Y puede generar un error de compilación debido a la dependencia faltante, X.
Este es un ejemplo de mensaje de error para módulos de Android.bp:
"Y" depends on undefined module "X"
Este es un ejemplo de mensaje de error para módulos Android.mk:
'.../JAVA_LIBRARIES/com.android.X_intermediates/dexpreopt.config', needed by '.../APPS/Y_intermediates/enforce_uses_libraries.status', missing and no known rule to make it
Una fuente común de estos errores ocurre cuando una biblioteca tiene un nombre diferente al de su
al módulo correspondiente se le asigna un nombre en el sistema de compilación. Por ejemplo, si la entrada <uses-library>
del manifiesto es com.android.X
, pero el nombre del módulo de la biblioteca es solo X
, se produce un error. Para resolver este caso, indícale al sistema de compilación que
el módulo llamado X
proporciona un <uses-library>
llamado com.android.X
.
Este es un ejemplo de bibliotecas Android.bp
(propiedad del módulo):
provides_uses_lib: “com.android.X”,
Este es un ejemplo de bibliotecas de Android.mk (variable de módulo):
LOCAL_PROVIDES_USES_LIBRARY := com.android.X
Discrepancia de CLC en el tiempo de inicio
En el primer inicio, busca en logcat mensajes relacionados con la discrepancia de CLC, como se muestra a continuación:
$ adb wait-for-device && adb logcat \
| grep -E 'ClassLoaderContext [a-z ]+ mismatch' -A1
El resultado puede tener mensajes como el que se muestra aquí:
[...] W system_server: ClassLoaderContext shared library size mismatch Expected=..., found=... (PCL[]... | PCL[]...)
[...] I PackageDexOptimizer: Running dexopt (dexoptNeeded=1) on: ...
Si recibes una advertencia de discrepancia de CLC, busca un comando dexopt para la módulo. Para solucionarlo, asegúrate de que se apruebe la verificación del tiempo de compilación del módulo. Si eso no funciona, es posible que el tuyo sea un caso especial que el sistema de compilación no admite (como una app que carga otro APK, no una biblioteca). El El sistema de compilación no maneja todos los casos, ya que durante el tiempo de compilación es imposible saber con certeza qué carga la app durante el tiempo de ejecución.
Contexto del cargador de clases
El CLC es una estructura en forma de árbol que describe la jerarquía del cargador de clases. El
usa CLC en un sentido limitado (solo abarca bibliotecas, no APK ni
de clase personalizada): un árbol de bibliotecas que representa
cierre de todas las dependencias de <uses-library>
de una biblioteca o app El nivel superior
elementos de un CLC son las dependencias <uses-library>
directas especificadas
en el manifiesto (la ruta de clase). Cada nodo de un árbol de CLC es un nodo <uses-library>
que puede tener sus propios subnodos <uses-library>
.
Debido a que las dependencias de <uses-library>
son un grafo acíclico dirigido y no necesariamente un árbol, el CLC puede contener varios subárboles para la misma biblioteca. En otras palabras, el CLC es el gráfico de dependencias “desplegado” en un árbol. La duplicación solo se produce a nivel lógico; los cargadores de clases subyacentes reales no se duplican (en el tiempo de ejecución, hay una sola instancia de cargador de clases para cada biblioteca).
CLC define el orden de búsqueda de las bibliotecas al resolver las clases de Java que usa la biblioteca o app. El orden de búsqueda es importante porque las bibliotecas pueden contener clases duplicadas y la clase se resuelve en la primera coincidencia.
CLC integrado en el dispositivo (tiempo de ejecución)
PackageManager
(en frameworks/base
) crea un CLC para cargar un módulo Java en el dispositivo. Agrega las bibliotecas que se enumeran en las etiquetas <uses-library>
en el manifiesto del módulo
como elementos CLC de nivel superior.
Para cada biblioteca utilizada, PackageManager
obtiene todas sus dependencias de <uses-library>
(especificadas como etiquetas en el manifiesto de esa biblioteca) y agrega un CLC anidado para cada dependencia. Este proceso continúa de forma recursiva hasta que todos los nodos hoja del árbol CLC construido sean bibliotecas sin dependencias de <uses-library>
.
PackageManager
solo reconoce las bibliotecas compartidas. La definición de “compartida”
este uso difiere de su significado habitual (como compartido frente a estático). En Android, las bibliotecas compartidas de Java son aquellas que se enumeran en las configuraciones XML que se instalan en el dispositivo (/system/etc/permissions/platform.xml
). Cada entrada contiene el nombre de una biblioteca compartida, una ruta de acceso a su archivo JAR DEX y una lista de dependencias (otras bibliotecas compartidas que esta usa en el tiempo de ejecución y especifica en las etiquetas <uses-library>
de su manifiesto).
En otras palabras, hay dos fuentes de información que permiten PackageManager
para construir CLC en el tiempo de ejecución: etiquetas <uses-library>
en el manifiesto
las dependencias de bibliotecas compartidas
en configuraciones XML.
CLC en el host (en el momento de la compilación)
El CLC no solo es necesario cuando se carga una biblioteca o una app, sino también cuando se compila una. La compilación puede ocurrir en el dispositivo (dexopt) o durante la compilación (dexpreopt). Como el dexopt se realiza en el dispositivo, tiene las mismas
información como PackageManager
(manifiestos y dependencias de bibliotecas compartidas).
Dexpreopt, en cambio, se lleva a cabo en el host y en un entorno
y debe obtener la misma información del sistema de compilación.
Por lo tanto, el CLC del tiempo de compilación que usa dexpreopt y el CLC del tiempo de ejecución que usa PackageManager
son lo mismo, pero se calculan de dos maneras diferentes.
Los CLC en el tiempo de compilación y en el entorno de ejecución deben coincidir; de lo contrario, el código compilado por AOT
que creó dexpreopt se rechazan. Para verificar la igualdad de los CLC del tiempo de compilación y del tiempo de ejecución, el compilador de dex2oat registra los CLC del tiempo de compilación en los archivos *.odex
(en el campo classpath
del encabezado del archivo OAT). Para encontrar el CLC almacenado, usa
este comando:
oatdump --oat-file=<FILE> | grep '^classpath = '
La discrepancia de CLC en el tiempo de compilación y el tiempo de ejecución se informa en logcat durante el inicio. Para buscarlo, usa el siguiente comando:
logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch'
La discrepancia es negativa para el rendimiento, ya que obliga a la biblioteca o app a dexoptado o ejecutarse sin optimizaciones (por ejemplo, el código de la app podría deben extraerse en la memoria del APK, una operación muy costosa).
Una biblioteca compartida puede ser opcional o obligatoria. Desde
el punto de vista de dexpreopt, una biblioteca obligatoria debe estar presente en el tiempo de compilación (su
la ausencia es un error de compilación). Una biblioteca opcional puede estar presente o ausente
durante la compilación: si está presente, se agrega al CLC, se pasa a dex2oat y
registrado en el archivo *.odex
. Si falta una biblioteca opcional, se omite y no se agrega al CLC. Si hay una discrepancia entre el estado del tiempo de compilación y el tiempo de ejecución (la biblioteca opcional está presente en un caso, pero no en el otro), los CLC del tiempo de compilación y el tiempo de ejecución no coinciden, y se rechaza el código compilado.
Detalles avanzados del sistema de compilación (corrector de manifiestos)
A veces, faltan etiquetas <uses-library>
en el manifiesto de origen de una biblioteca o app. Esto puede suceder, por ejemplo, si una de las dependencias transitivas de la biblioteca o app comienza a usar otra etiqueta <uses-library>
y el manifiesto de la biblioteca o app no se actualiza para incluirla.
Soong puede calcular automáticamente algunas de las etiquetas <uses-library>
faltantes para una biblioteca o app determinada, como las bibliotecas del SDK en el cierre de dependencia transitiva de la biblioteca o app. El cierre es necesario porque la biblioteca (o app) puede depender de una biblioteca estática que depende de una biblioteca del SDK y, posiblemente, vuelva a depender de forma transitiva a través de otra biblioteca.
No todas las etiquetas <uses-library>
se pueden procesar de esta manera, pero cuando es posible, es
es preferible permitir que Soong agregue entradas de manifiesto automáticamente. es menos
propenso a errores y simplifica el mantenimiento. Por ejemplo, cuando muchas apps usan un
que agrega una nueva dependencia <uses-library>
, todas las apps deben
se actualizan, lo cual es difícil de mantener.