Das Build-System von Android 12 wurde für die AOT-Kompilierung von DEX-Dateien (dexpreopt) für Java-Module mit <uses-library>
-Abhängigkeiten geändert. In einigen Fällen können diese Änderungen am Buildsystem zu Build-Fehlern führen. Auf dieser Seite kannst du dich auf Brüche vorbereiten und den Anleitungen folgen, um sie zu beheben und zu minimieren.
Dexpreopt ist der Prozess, bei dem Java-Bibliotheken und ‑Apps vorab kompiliert werden. Dexpreopt erfolgt während der Build-Erstellung auf dem Host – im Gegensatz zu dexopt, auf dem Gerät passiert). Die Struktur der Abhängigkeiten von freigegebenen Bibliotheken, die von einem Java-Modul (einer Bibliothek oder einer App) verwendet werden, wird als Klassenladekontext (CLC) bezeichnet. Damit dexpreopt korrekt funktioniert, müssen die CLCs der Build- und Laufzeit übereinstimmen. Die CLC zur Buildzeit wird vom dex2oat-Compiler zum Zeitpunkt der dexpreopt-Phase verwendet (sie wird in den ODEX-Dateien aufgezeichnet). Die CLC zur Laufzeit ist der Kontext, in dem der vorab kompilierte Code auf dem Gerät geladen wird.
Diese CLCs für Build- und Laufzeit müssen sowohl aus Gründen der Korrektheit als auch der Leistung übereinstimmen. Es ist notwendig, doppelte Klassen zu verarbeiten, damit sie korrekt sind. Wenn Die Abhängigkeiten der gemeinsam genutzten Bibliothek zur Laufzeit unterscheiden sich von denen, die für werden einige Klassen möglicherweise anders aufgelöst, was zu subtilen Laufzeitfehlern. Die Leistung wird auch durch die Laufzeitprüfungen auf doppelte Klassen beeinträchtigt.
Betroffene Anwendungsfälle
Der erste Start ist der Hauptanwendungsfall, der von diesen Änderungen betroffen ist: Wenn ART eine Abweichung zwischen CLCs der Build- und Laufzeit erkennt, werden dexpreopt-Artefakte abgelehnt und stattdessen dexopt ausgeführt. Bei nachfolgenden Starts ist das in Ordnung, da die Apps im Hintergrund dexoptiert und auf dem Laufwerk gespeichert werden können.
Betroffene Bereiche von Android
Dies betrifft alle Java-Anwendungen und -Bibliotheken, bei denen Laufzeitabhängigkeiten von anderen Java-Bibliotheken. Es gibt Tausende von Android-Apps, von denen Hunderte verwendet werden. gemeinsam genutzten Bibliotheken. Partner sind ebenfalls betroffen, da sie ihre eigenen Bibliotheken und Apps.
Änderungen für Werbeunterbrechung
Das Build-System muss vorher die <uses-library>
-Abhängigkeiten kennen
Erzeugt dexpreopt-Build-Regeln. Es kann jedoch nicht direkt auf das Manifest zugegriffen werden.
und lies die <uses-library>
enthalten, da das Build-System keine beliebigen Dateien lesen darf,
werden aus Leistungsgründen Build-Regeln generiert. Außerdem kann das Manifest in einem APK oder einem Prebuilt-Objekt verpackt sein. Dementsprechend entspricht der <uses-library>
Informationen müssen in den Build-Dateien vorhanden sein (Android.bp
oder Android.mk
).
Bisher wurde in ART eine Umgehung verwendet, bei der Abhängigkeiten von freigegebenen Bibliotheken (&-classpath
) ignoriert wurden. Dies war unsicher und führte zu subtilen Fehlern. Daher wurde die Umgehung in Android 12 entfernt.
Dies hat zur Folge, dass Java-Module, die keine korrekte <uses-library>
Informationen in ihren Build-Dateien können Build-Ausfälle verursachen (durch einen
CLC-Abweichung bei der Build-Erstellung) oder Regressionen beim ersten Start (verursacht durch einen Startvorgang)
CLC-Abweichung, gefolgt von „dexopt“.
Migrationspfad
Führen Sie die folgenden Schritte aus, um einen fehlerhaften Build zu reparieren:
So deaktivieren Sie die Buildzeitprüfung für ein bestimmtes Produkt global:
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
im Produkt-Makefile. Dadurch werden Buildfehler behoben (außer in Sonderfällen, die im Abschnitt Fehler beheben aufgeführt sind). Sie können jedoch Dies ist eine vorübergehende Problemumgehung und kann zu einer Abweichung der CLC beim Start und dann auf Dexopt.
Fehler bei Modulen beheben, bevor Sie die Build-Zeitprüfung global deaktiviert haben durch Hinzufügen der erforderlichen
<uses-library>
-Informationen zu ihren Build-Dateien hinzufügen. Weitere Informationen finden Sie unter Fehler beheben. Bei den meisten Modulen müssen Sie hier einige Zeilen inAndroid.bp
oder inAndroid.mk
.Deaktivieren Sie die Buildzeitprüfung und dexpreopt für die problematischen Fälle auf Modulebene. Deaktivieren Sie dexpreopt, damit Sie keine Build-Zeit verschwenden Speicher für Artefakte, die beim Booten abgelehnt werden.
Deaktivieren Sie die Option, um die Build-Zeitprüfung global wieder zu aktivieren.
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES
, der in Schritt 1 festgelegt wurde; der Build nach dieser Änderung nicht fehlschlagen (anhand der Schritte 2 und 3).Korrigieren Sie die in Schritt 3 deaktivierten Module einzeln und aktivieren Sie sie dann wieder. dexpreopt und die
<uses-library>
-Prüfung. Reichen Sie bei Bedarf Fehler ein.
In Android 12 werden <uses-library>
-Prüfungen während der Build-Zeit erzwungen.
Probleme beheben
In den folgenden Abschnitten erfahren Sie, wie Sie bestimmte Arten von Unterbrechungen beheben.
Build-Fehler: CLC-Nichtübereinstimmung
Das Build-System führt eine Kohärenzprüfung zur Build-Zeit zwischen den Informationen in
Android.bp
- oder Android.mk
-Dateien und das Manifest. Das Build-System kann keine Daten
das Manifest, kann aber Build-Regeln zum Lesen des Manifests generieren (Extrahieren
gegebenenfalls von einem APK) und vergleiche die <uses-library>
-Tags im Manifest
mit den <uses-library>
-Informationen in den Build-Dateien vergleichen. Wenn die Prüfung fehlschlägt,
sieht der Fehler so aus:
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
Wie die Fehlermeldung verdeutlicht, gibt es mehrere Lösungen, je nachdem, Dringlichkeit:
- Legen Sie für eine vorübergehende produktweite Korrektur fest:
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
im Produkt-Makefile. Die Kohärenzprüfung bei der Build-Erstellung wird weiterhin ausgeführt, aber ein Prüfungsfehler bedeutet nicht, dass Build-Fehler. Stattdessen führt ein Prüffehler dazu, dass das Build-System den dex2oat-Compilerfilter in dexpreopt aufverify
herabstuft, wodurch die AOT-Kompilierung für dieses Modul vollständig deaktiviert wird. - Für eine schnelle globale Befehlszeilenkorrektur verwenden Sie die Umgebungsvariable
RELAX_USES_LIBRARY_CHECK=true
Es hat denselben Effekt wiePRODUCT_BROKEN_VERIFY_USES_LIBRARIES
, ist aber für die Verwendung in der Befehlszeile vorgesehen. Die die Produktvariable überschreiben. - Um eine Lösung zur Behebung der Ursache des Fehlers zu erhalten, informieren Sie das Build-System über
<uses-library>
-Tags im Manifest. Aus der Fehlermeldung geht hervor, welche Bibliotheken das Problem verursachen. Das gilt auch für die Prüfung vonAndroidManifest.xml
oder des Manifests in einem APK, das mitaapt dump badging $APK | grep uses-library
geprüft werden kann.
Für Android.bp
-Module:
Suchen Sie im Attribut
libs
des Moduls nach der fehlenden Bibliothek. Wenn es fügt Soong diese Bibliotheken normalerweise automatisch hinzu, außer in diesen Sonderfälle:- Die Bibliothek ist keine SDK-Bibliothek. Sie ist als
java_library
definiert, alsjava_sdk_library
). - Die Bibliothek hat im Manifest einen anderen Namen für die Bibliothek als ihr Modul. Name (im Build-System).
Fügen Sie
provides_uses_lib: "<library-name>"
hinzu, um das Problem vorübergehend zu beheben.Android.bp
-Bibliotheksdefinition. Um eine langfristige Lösung zu erhalten, müssen Sie die zugrunde liegende Problem: Konvertieren Sie die Bibliothek in eine SDK-Bibliothek oder benennen Sie ihr Modul um.- Die Bibliothek ist keine SDK-Bibliothek. Sie ist als
Wenn das Problem durch den vorherigen Schritt nicht behoben wurde, fügen Sie der
Android.bp
-Definition des Modulsuses_libs: ["<library-module-name>"]
für erforderliche Bibliotheken oderoptional_uses_libs: ["<library-module-name>"]
für optionale Bibliotheken hinzu. Für diese Properties ist eine Liste zulässig von Modulnamen. Die relative Reihenfolge der Bibliotheken auf der Liste muss gleich sein als Reihenfolge im Manifest.
Für Android.mk
-Module:
Prüfen Sie, ob die Bibliothek im Manifest einen anderen Namen für die Bibliothek hat als ihr Modulname (im Build-System). Falls ja, können Sie das Problem vorübergehend beheben, indem Sie
LOCAL_PROVIDES_USES_LIBRARY := <library-name>
in der DateiAndroid.mk
von oder fügeprovides_uses_lib: "<library-name>"
imAndroid.bp
hinzu der Bibliothek (beide Fälle sind möglich, da einAndroid.mk
-Modul ist möglicherweise von einerAndroid.bp
-Bibliothek abhängig). Für eine langfristige Lösung beheben Sie das zugrunde liegende Problem: Benennen Sie das Bibliotheksmodul um.Fügen Sie der
Android.mk
-Definition des ModulsLOCAL_USES_LIBRARIES := <library-module-name>
für erforderliche Bibliotheken undLOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name>
für optionale Bibliotheken hinzu. Diese -Properties akzeptieren eine Liste von Modulnamen. Die relative Reihenfolge der Bibliotheken in der Liste muss mit der im Manifest übereinstimmen.
Build-Fehler: unbekannter Bibliothekspfad
Wenn das Build-System keinen Pfad zu einer <uses-library>
-DEX-JAR-Datei findet (entweder ein
oder einen Installationspfad auf dem Gerät)
erstellen. Wenn kein Pfad gefunden wird, kann das darauf hindeuten, dass die Bibliothek auf unerwartete Weise konfiguriert ist. Korrigieren Sie den Build vorübergehend, indem Sie „dexpreopt“ für das problematische Element deaktivieren
-Modul.
Android.bp (Moduleigenschaften):
enforce_uses_libs: false,
dex_preopt: {
enabled: false,
},
Android.mk (Modulvariablen):
LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false
Melden Sie einen Fehler, um nicht unterstützte Szenarien zu untersuchen.
Build-Fehler: Bibliotheksabhängigkeit fehlt
Ein Versuch, <uses-library>
X aus dem Manifest des Moduls Y der Builddatei für Y hinzuzufügen, kann aufgrund der fehlenden Abhängigkeit X zu einem Buildfehler führen.
Hier ist ein Beispiel für eine Fehlermeldung für Android.bp-Module:
"Y" depends on undefined module "X"
Hier ist eine Beispielfehlermeldung für Android.mk-Module:
'.../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
Eine häufige Ursache für solche Fehler ist, wenn eine Bibliothek anders benannt ist als das entsprechende Modul im Build-System. Wenn zum Beispiel das Manifest
Der <uses-library>
-Eintrag ist com.android.X
, der Name des Bibliotheksmoduls ist jedoch
nur X
enthält, wird ein Fehler verursacht. Um diesen Fall zu lösen, teilen Sie dem Build-System mit,
Das Modul X
bietet eine <uses-library>
mit dem Namen com.android.X
.
Hier ein Beispiel für Android.bp
-Bibliotheken (Moduleigenschaft):
provides_uses_lib: “com.android.X”,
Hier ist ein Beispiel für Android.mk-Bibliotheken (Modulvariable):
LOCAL_PROVIDES_USES_LIBRARY := com.android.X
Abweichender CLC beim Start
Suchen Sie beim ersten Start in logcat nach Meldungen im Zusammenhang mit einer CLC-Nichtübereinstimmung, wie unten dargestellt:
$ adb wait-for-device && adb logcat \
| grep -E 'ClassLoaderContext [a-z ]+ mismatch' -A1
Die Ausgabe kann Meldungen vom folgenden Format enthalten:
[...] W system_server: ClassLoaderContext shared library size mismatch Expected=..., found=... (PCL[]... | PCL[]...)
[...] I PackageDexOptimizer: Running dexopt (dexoptNeeded=1) on: ...
Wenn Sie eine CLC-Nichtübereinstimmungswarnung erhalten, suchen Sie nach einem dexopt-Befehl für das fehlerhafte Modul. Um das Problem zu beheben, muss die Buildzeitprüfung für das Modul bestanden werden. Wenn das nicht funktioniert, handelt es sich möglicherweise um einen Sonderfall, der vom Build-System nicht unterstützt wird, z. B. eine App, die ein anderes APK und keine Bibliothek lädt. Das Build-System kann nicht alle Fälle verarbeiten, da zum Zeitpunkt der Erstellung nicht mit Sicherheit bekannt ist, was die App zur Laufzeit lädt.
Kontext des Klassenladers
Die CLC ist eine baumartige Struktur, die die Hierarchie des Klassenladeprogramms beschreibt. Die
verwendet CLC im engeren Sinne. Es deckt nur Bibliotheken ab, keine APKs oder
loaders benutzerdefinierter Klasse): Hierbei handelt es sich um eine Baumstruktur von Bibliotheken, die transitive
Schließung aller <uses-library>
-Abhängigkeiten einer Bibliothek oder Anwendung. Die oberste Ebene
Elemente einer CLC sind die direkt angegebenen <uses-library>
-Abhängigkeiten.
im Manifest (Klassenpfad) ein. Jeder Knoten eines CLC-Baums ist ein <uses-library>
-Knoten, der eigene <uses-library>
-Unterknoten haben kann.
Da <uses-library>
-Abhängigkeiten ein gerichteter azyklischer Graph sind,
eine Baumstruktur ist, kann CLC
mehrere Unterstrukturen für dieselbe Bibliothek enthalten. In
Mit anderen Worten, CLC ist das Abhängigkeitsdiagramm bis zu einem Baum. Die Duplizierung erfolgt nur auf logischer Ebene. Die zugrunde liegenden Class Loader werden nicht dupliziert. Bei der Laufzeit gibt es für jede Bibliothek eine einzelne Class Loader-Instanz.
CLC definiert die Suchreihenfolge von Bibliotheken beim Auflösen von Java-Klassen, die von der Bibliothek oder App verwendet werden. Die Suchreihenfolge ist wichtig, da Bibliotheken doppelte Klassen enthalten können und die Klasse auf die erste Übereinstimmung aufgelöst wird.
CLC auf dem Gerät (Laufzeit)
PackageManager
(in frameworks/base
) erstellt ein CLC, um ein Java-Modul auf dem Gerät zu laden. Die im Manifest des Moduls in den <uses-library>
-Tags aufgeführten Bibliotheken werden als CLC-Elemente der obersten Ebene hinzugefügt.
Für jede verwendete Bibliothek erhält PackageManager
alle zugehörigen <uses-library>
Abhängigkeiten (definiert als Tags im Manifest dieser Bibliothek) und fügt ein
verschachtelte CLC für jede Abhängigkeit. Dieser Vorgang wird rekursiv fortgesetzt, bis alle Blätterknoten des erstellten CLC-Baums Bibliotheken ohne <uses-library>
-Abhängigkeiten sind.
PackageManager
erkennt nur geteilte Fotogalerien. Die Definition von „freigegeben“ in dieser Verwendung unterscheidet sich von der üblichen Bedeutung (z. B. „freigegeben“ im Vergleich zu „statisch“). In Android sind Java-freigegebene Bibliotheken die in XML-Konfigurationen aufgeführten, die auf dem Gerät installiert sind (/system/etc/permissions/platform.xml
). Jeder Eintrag enthält den Namen einer freigegebenen Bibliothek, einen Pfad zu ihrer DEX-JAR-Datei und eine Liste der Abhängigkeiten (andere freigegebene Bibliotheken, die diese Bibliothek zur Laufzeit verwendet und in <uses-library>
-Tags im Manifest angibt).
Mit anderen Worten: Es gibt zwei Informationsquellen, die es ermöglichen, PackageManager
zum Erstellen einer CLC zur Laufzeit: <uses-library>
-Tags im Manifest und
der gemeinsam genutzten Bibliothek
in XML-Konfigurationen.
On-Host-CLC (Buildzeit)
CLC ist nicht nur beim Laden einer Bibliothek oder App erforderlich, sondern auch beim Kompilieren. Die Kompilierung kann entweder auf dem Gerät (Dexopt) oder während
build (dexpreopt). Da die Dexopt-Funktion auf dem Gerät stattfindet,
Informationen als PackageManager
(Abhängigkeiten von Manifesten und gemeinsam genutzten Bibliotheks).
Dexpreopt findet jedoch on-host und in einer völlig anderen Umgebung statt und muss dieselben Informationen vom Build-System abrufen.
Daher sind die von dexpreopt verwendete CLC zur Buildzeit und die von PackageManager
verwendete CLC zur Laufzeit dasselbe, werden aber auf zwei unterschiedliche Arten berechnet.
Build-Time- und Laufzeit-CLCs müssen übereinstimmen, andernfalls der über AOT kompilierte Code
von dexpreopt erstellt, wird abgelehnt. Um die Gleichheit von Build-Zeit
Laufzeit-CLCs, zeichnet der dex2oat-Compiler zur Erstellungszeit die CLC in den *.odex
-Dateien auf.
(im Feld classpath
der Kopfzeile der OAT-Datei). Verwenden Sie den folgenden Befehl, um den gespeicherten CLC zu finden:
oatdump --oat-file=<FILE> | grep '^classpath = '
Beim Booten wird in Logcat eine Diskrepanz zwischen Build-Zeit und Laufzeit-CLC gemeldet. Suchen Sie mit diesem Befehl danach:
logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch'
Eine Abweichung wirkt sich negativ auf die Leistung aus, da die Bibliothek oder App entweder deoptimiert oder ohne Optimierungen ausgeführt werden muss. Beispielsweise muss der Code der App möglicherweise aus dem APK in den Arbeitsspeicher extrahiert werden, was sehr ressourcenintensiv ist.
Eine gemeinsam genutzte Bibliothek kann optional oder erforderlich sein. Von
Aus dexpreopt-Perspektive muss eine erforderliche Bibliothek zum Zeitpunkt der Erstellung vorhanden sein (ihre
Fehlen ist ein Build-Fehler). Eine optionale Bibliothek kann vorhanden oder nicht vorhanden sein
bei der Build-Erstellung: Falls vorhanden, wird er der CLC hinzugefügt, an dex2oat übergeben und
in der Datei *.odex
aufgezeichnet. Wenn eine optionale Bibliothek fehlt, wird sie übersprungen und nicht der CLC hinzugefügt. Wenn der Status der Build- und Laufzeit nicht übereinstimmt (die optionale Bibliothek ist in einem Fall vorhanden, im anderen nicht), stimmen die CLCs der Build- und Laufzeit nicht überein und der kompilierte Code wird abgelehnt.
Details zum erweiterten Buildsystem (Manifest-Fixer)
Manchmal fehlen <uses-library>
-Tags im Quellmanifest einer Bibliothek oder App. Das kann beispielsweise passieren, wenn eine der übergeordneten Abhängigkeiten der Bibliothek oder App ein anderes <uses-library>
-Tag verwendet und das Manifest der Bibliothek oder App nicht entsprechend aktualisiert wird.
Soong kann einige der fehlenden <uses-library>
-Tags für eine bestimmte Bibliothek oder App automatisch berechnen, da die SDK-Bibliotheken in der transitiven Abhängigkeitsschließung der Bibliothek oder App enthalten sind. Die Schließung ist erforderlich, da die Bibliothek (oder App) möglicherweise von einer statischen Bibliothek abhängt, die von einer SDK-Bibliothek abhängt, und möglicherweise wiederum transitiv über eine andere Bibliothek abhängt.
Nicht alle <uses-library>
-Tags können auf diese Weise berechnet werden. Soong sollte jedoch nach Möglichkeit automatisch Manifesteinträge hinzufügen, da dies weniger fehleranfällig ist und die Wartung vereinfacht. Wenn viele Apps z. B. ein statisches
Bibliothek hinzugefügt, die eine neue <uses-library>
-Abhängigkeit hinzufügt, müssen alle Apps
was schwer zu verwalten ist.