W Androidzie 12 wprowadzono zmiany w systemie kompilacji w kompilacji AOT
plików DEX (dexpreopt) dla modułów Java z <uses-library>
zależności. W niektórych przypadkach zmiany w systemie kompilacji mogą spowodować przerwanie kompilacji. Na tej stronie możesz się przygotować na przerwy w działaniu usługi i zapoznać się z metodami ich rozwiązywania i minimalizowania.
Dexpreopt to proces z wyprzedzeniem kompilowania bibliotek Java i aplikacji. Metoda Dexpreopt jest przeprowadzana na hoście w momencie kompilacji (w przeciwieństwie do metody dexopt, która odbywa się na urządzeniu). Struktura zależności bibliotek udostępnionych w Javie (biblioteka lub aplikacja) jest określany jako kontekst wczytywania klasy (CLC). Do muszą zagwarantować prawidłowość dexpreopt, czas kompilacji i czas działania CLC pokrywają się. CLC w czasie kompilacji to wartość używana przez kompilator dex2oat w czasie dexpreopt (jest zapisywana w plikach ODEX), a CLC w czasie wykonywania to kontekst, w którym skompilowany kod jest ładowany na urządzeniu.
Te interfejsy CLC zarówno w czasie kompilacji, jak i w czasie wykonywania muszą zbiegać się ze sobą z obu powodów i wydajność kampanii. Aby zapewnić poprawność, należy obsłużyć zduplikowane klasy. Jeśli zależności od współdzielonej biblioteki w czasie wykonywania są inne niż te używane do kompilacji, niektóre klasy mogą być rozwiązywane inaczej, co może powodować drobne błędy w czasie wykonywania. Na wydajność wpływa też sprawdzanie w czasie działania pod kątem duplikatów. zajęcia.
Przypadki użycia, których dotyczy problem
Pierwszy rozruch to główny przypadek użycia, którego dotyczą te zmiany: jeśli ART wykrywa niezgodność między interfejsami CLC w czasie kompilacji i w czasie wykonywania, odrzuca dexpreopt i uruchamiać dexopt. W przypadku kolejnych rozruchów nie ma to znaczenia, ponieważ aplikacje mogą być dexoptowane w tle i przechowywane na dysku.
Obszary na Androidzie, których dotyczy problem
Ma to wpływ na wszystkie biblioteki i aplikacje Java, których działanie zależy od środowiska wykonawczego z innymi bibliotekami Java. Na Androidzie są tysiące aplikacji, z których setki korzystają biblioteki udostępnione. Dotyczy to też partnerów, którzy mają własne biblioteki i aplikacje.
Zmiana powodująca niezgodność
System kompilacji musi znać zależności <uses-library>
, zanim zacznie działać
generuje reguły kompilacji dexpreopt. Nie ma jednak bezpośredniego dostępu do pliku manifestu.
i przeczytaj <uses-library>
tagów, ponieważ system kompilacji nie może odczytać dowolnych plików, gdy
generuje reguły kompilacji (ze względu na wydajność). Plik manifestu może też
może być spakowana w pliku APK lub gotowym pliku. Dlatego parametr <uses-library>
w plikach kompilacji muszą znajdować się informacje (Android.bp
lub Android.mk
).
Wcześniej ART stosował obejście, które ignorowało zależności zasobów wspólnych (znane
jako &-classpath
). Było to niebezpieczne i spowodowało drobne błędy, więc obejście problemu
została usunięta na Androidzie 12.
W rezultacie moduły Java, które nie podają prawidłowych informacji <uses-library>
w plikach kompilacji, mogą powodować błędy kompilacji (spowodowane przez niezgodność CLC w czasie kompilacji) lub regresje podczas pierwszego uruchomienia (spowodowane przez niezgodność CLC w czasie uruchamiania, po której następuje dexopt).
Ścieżka migracji
Aby naprawić uszkodzoną wersję kompilacji:
Globalnie wyłącz kontrolę w czasie kompilacji konkretnej usługi przez ustawienie
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
w pliku make produktu. Poprawia to błędy kompilacji (oprócz szczególnych przypadków, wymienionych w sekcji Usuwanie usterek). Jest to jednak tymczasowe obejście problemu, które może spowodować niezgodność CLC podczas uruchamiania, a następnie dexopt.
Napraw moduły, w których wystąpiły błędy, zanim globalnie wyłączysz kontrolę w czasie kompilacji. dodając niezbędne informacje
<uses-library>
i plikami kompilacji (szczegóły znajdziesz w artykule Usuwanie usterek). W przypadku większości modułów wymaga to dodania kilku wierszy w językuAndroid.bp
lub wAndroid.mk
W przypadkach problematycznych wyłącz kontrolę w czasie kompilacji i dyrektywę dexpreopt. z każdym modułem. Wyłącz dexpreopt, aby nie marnować czasu kompilacji i miejsca na dane na artefakty, które są odrzucane podczas uruchamiania.
Ponownie włącz globalnie sprawdzanie w czasie kompilacji, usuwając ustawienie
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES
z kroku 1. Po tej zmianie kompilacja nie powinna się nie udać (z powodu kroków 2 i 3).Popraw po kolei moduły wyłączone w kroku 3, a następnie ponownie je włączaj dexpreopt oraz test
<uses-library>
. W razie potrzeby zgłaszaj błędy.
W Androidzie 12 są wymuszane kontrole <uses-library>
w czasie kompilacji.
Naprawianie uszkodzeń
W sekcjach poniżej dowiesz się, jak naprawić konkretne typy uszkodzeń.
Błąd kompilacji: niezgodność CLC
System kompilacji sprawdza spójność w czasie kompilacji między informacjami w
Pliki (Android.bp
lub Android.mk
) i plik manifestu. System kompilacji nie może odczytać pliku manifestu, ale może wygenerować reguły kompilacji, aby odczytać manifest (w razie potrzeby wyodrębniając go z pliku APK), i porównać tagi <uses-library>
w pliku manifestu z informacjami <uses-library>
w plikach kompilacji. Jeśli sprawdzenie się nie powiedzie, błąd będzie wyglądał tak:
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
Jak wskazuje komunikat o błędzie, jest wiele rozwiązań w zależności od pilność:
- Aby uzyskać tymczasową poprawkę w całym produkcie, ustaw wartość
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
w pliku marki produktu. Sprawdzanie spójności w czasie kompilacji jest nadal wykonywane, ale jego niepowodzenie nie oznacza niepowodzenia kompilacji. Zamiast tego błąd sprawdzania powoduje, że system kompilacji jest filtr kompilatora dex2oat naverify
w dexpreopt, który wyłącza kompilację AOT w całości tego modułu. - Aby wprowadzić szybką, globalną poprawkę w wierszu poleceń, użyj zmiennej środowiskowej
RELAX_USES_LIBRARY_CHECK=true
. Ma on ten sam efekt coPRODUCT_BROKEN_VERIFY_USES_LIBRARIES
, ale jest przeznaczony do użycia w wierszu poleceń. Zmienne środowiskowe zastępują zmienne produktu. - Aby naprawić błąd u źródła, wskaż systemowi kompilacji tagi
<uses-library>
w pliku manifestu. Komunikat o błędzie wskazuje, które biblioteki powodują problem (podobnie jakAndroidManifest.xml
lub plik manifestu w pliku APK, który można sprawdzić za pomocą polecenia `aapt dump badging $APK | grep uses-library
`).
W przypadku modułów (Android.bp
):
Poszukaj brakującej biblioteki we właściwości
libs
modułu. Jeśli tak Song zwykle automatycznie dodaje takie biblioteki automatycznie, chyba że w tych wyjątkowe przypadki:- Ta biblioteka nie jest biblioteką pakietu SDK (jest zdefiniowana jako
java_library
, niżjava_sdk_library
). - Biblioteka ma inną nazwę biblioteki (w pliku manifestu) niż nazwa modułu (w systemie kompilacji).
Aby tymczasowo rozwiązać ten problem, dodaj
provides_uses_lib: "<library-name>"
w Definicja bibliotekiAndroid.bp
. Aby to zrobić długoterminowo, rozwiąż problem problem: przekonwertuj bibliotekę na bibliotekę SDK lub zmień nazwę modułu.- Ta biblioteka nie jest biblioteką pakietu SDK (jest zdefiniowana jako
Jeśli poprzedni krok nie rozwiązał problemu, do definicji
Android.bp
modułu dodajuses_libs: ["<library-module-name>"]
w przypadku wymaganych bibliotek luboptional_uses_libs: ["<library-module-name>"]
w przypadku opcjonalnych bibliotek. Te miejsca zakwaterowania akceptują listę nazwy modułów. Względna kolejność bibliotek na liście musi być taka sama zgodnie z kolejnością w pliku manifestu.
W przypadku modułów (Android.mk
):
Sprawdź, czy biblioteka ma inną nazwę (w pliku manifestu) niż jej nazwa nazwy modułu (w systemie kompilacji). Jeśli tak, rozwiąż ten problem tymczasowo, dodając
LOCAL_PROVIDES_USES_LIBRARY := <library-name>
w plikuAndroid.mk
w bibliotece lub dodajprovides_uses_lib: "<library-name>"
wAndroid.bp
biblioteki (można to zrobić w obu przypadkach, ponieważ modułAndroid.mk
może zależeć od bibliotekiAndroid.bp
). Aby rozwiązać problem na stałe, napraw problem podstawowy: zmień nazwę modułu biblioteki.Dodaj
LOCAL_USES_LIBRARIES := <library-module-name>
dla wymaganych bibliotek; dodajLOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name>
dla opcjonalnych bibliotek do definicjiAndroid.mk
modułu. Te właściwości akceptują listę nazw modułów. Kolejność bibliotek na liście musi być taka sama jak w pliku manifestu.
Błąd kompilacji: nieznany ścieżka biblioteki
Jeśli system kompilacji nie może znaleźć ścieżki do pliku <uses-library>
DEX jar (ścieżki kompilacji na hoście lub ścieżki instalacji na urządzeniu), kompilacja zwykle się nie powiedzie. Jeśli nie udało się znaleźć ścieżki, może to oznaczać, że biblioteka jest skonfigurowana w
w nieoczekiwany sposób. Tymczasowo napraw kompilację, wyłączając dexpreopt dla problematycznego modułu.
Android.bp (właściwości modułu):
enforce_uses_libs: false,
dex_preopt: {
enabled: false,
},
Android.mk (zmienne modułu):
LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false
Zgłoś błąd, aby zbadać nieobsługiwane scenariusze.
Błąd kompilacji: brak zależności biblioteki
Próba dodania do pliku kompilacji dla Y pakietu <uses-library>
X z pliku manifestu modułu Y może spowodować błąd kompilacji z powodu braku zależności X.
Oto przykładowy komunikat o błędzie w przypadku modułów Android.bp:
"Y" depends on undefined module "X"
Oto przykładowy komunikat o błędzie dotyczący modułów 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
Częstym źródłem takich błędów jest inna nazwa biblioteki niż jej nazwa
odpowiadający modułowi jest nazwany w systemie kompilacji. Jeśli na przykład plik manifestu
Pozycja <uses-library>
to com.android.X
, ale nazwa modułu biblioteki to
X
powoduje błąd. Aby rozwiązać ten problem, powiedz systemowi kompilacji, że
moduł o nazwie X
zawiera <uses-library>
o nazwie com.android.X
.
Oto przykład bibliotek Android.bp
(właściwości modułu):
provides_uses_lib: “com.android.X”,
Oto przykład bibliotek Android.mk (zmienne modułu):
LOCAL_PROVIDES_USES_LIBRARY := com.android.X
Niezgodność CLC w czasie uruchamiania
Przy pierwszym uruchomieniu wyszukaj w narzędziu Logcat komunikaty związane z niezgodnością CLC, jak pokazano poniżej:
$ adb wait-for-device && adb logcat \
| grep -E 'ClassLoaderContext [a-z ]+ mismatch' -A1
Dane wyjściowe mogą zawierać komunikaty w takim formacie:
[...] W system_server: ClassLoaderContext shared library size mismatch Expected=..., found=... (PCL[]... | PCL[]...)
[...] I PackageDexOptimizer: Running dexopt (dexoptNeeded=1) on: ...
Jeśli pojawi się ostrzeżenie o niezgodności CLC, poszukaj polecenia dexopt dla wadliwego modułu. Aby rozwiązać ten problem, sprawdź, czy moduł przeszedł weryfikację w czasie kompilacji. Jeśli to nie zadziała, być może Twój przypadek jest szczególny i nie jest obsługiwany przez system kompilacji (np. aplikacja, która wczytuje inny plik APK, a nie bibliotekę). System kompilacji nie obsługuje wszystkich przypadków, ponieważ w momencie kompilacji nie można z pewnością stwierdzić, co aplikacja wczytuje w czasie działania.
Kontekst ładowarki klas
CLC to struktura w kształcie drzewa, która opisuje hierarchię modułu ładowania klas.
system kompilacji używa CLC w wąskim sensie (obejmuje tylko biblioteki, nie pliki APK
ładowarki klasy niestandardowej): jest to drzewo bibliotek, które reprezentuje
zamknięcie wszystkich zależności <uses-library>
biblioteki lub aplikacji. Organizacja najwyższego poziomu
elementami CLC są określone bezpośrednie zależności <uses-library>
w pliku manifestu (ścieżka klasy). Każdy węzeł drzewa CLC to węzeł <uses-library>
, który może mieć własne węzły podrzędne <uses-library>
.
Ponieważ zależności <uses-library>
to skierowany graf acykliczny, a niekoniecznie drzewo, CLC może zawierać wiele poddrzew w ramach tej samej biblioteki. W
Innymi słowy, CLC jest „rozwiniętym” grafem zależności. do drzewa. Podwójne ładowanie występuje tylko na poziomie logicznym. Rzeczywiste ładowarki klas nie są dublowane (w czasie wykonywania dla każdej biblioteki jest pojedyncza instancja ładowarki klas).
CLC określa kolejność wyszukiwania bibliotek podczas rozwiązywania klas Java używanych przez bibliotekę lub aplikację. Kolejność wyszukiwania jest ważna, ponieważ biblioteki mogą zawierać zduplikowane klasy, a klasa jest rozwiązywana do pierwszego dopasowania.
CLC na urządzeniu (w czasie działania)
PackageManager
(w frameworks/base
) tworzy interfejs CLC do wczytywania modułu Java
na urządzeniu. Dodaje biblioteki wymienione w tagach <uses-library>
w pliku manifestu modułu jako elementy CLC najwyższego poziomu.
W przypadku każdej użytej biblioteki PackageManager
pobiera wszystkie <uses-library>
zależności (określone jako tagi w pliku manifestu tej biblioteki) i dodaje do nich zagnieżdżoną CLC. Ten proces jest powtarzany rekurencyjnie, dopóki wszystkie liście węzły skonstruowanego drzewa CLC nie będą bibliotekami bez zależności <uses-library>
.
PackageManager
ma informacje o tylko bibliotekach udostępnionych. Definicja udostępniania w tym przypadku różni się od zwykłego znaczenia tego słowa (w opozycji do stałego). W Androidzie biblioteki współdzielone Javy to te, które są wymienione w konfiguracjach XML i zainstalowane na urządzeniu (/system/etc/permissions/platform.xml
). Każdy wpis zawiera nazwę biblioteki współdzielonej, ścieżkę do jej pliku JAR DEX oraz listę zależności (innych bibliotek współdzielonych, których używa ona w czasie wykonywania, i które są określone w tagach <uses-library>
w pliku manifestu).
Innymi słowy, istnieją 2 źródła informacji, które umożliwiają PackageManager
tworzenie CLC w czasie wykonywania: <uses-library>
tagi w pliku manifestu i zależności bibliotek współdzielonych w konfiguracjach XML.
CLC na hoście (w czasie kompilacji)
CLC jest potrzebny nie tylko podczas wczytywania biblioteki lub aplikacji, ale też podczas jej kompilowania. Kompilacja może odbywać się na urządzeniu (dexopt) lub podczas kompilacji (dexpreopt). Dexopt odbywa się na urządzeniu, więc ma taki sam
informacje jako PackageManager
(pliki manifestu i zależności bibliotek udostępnionych).
Metoda Dexpreopt odbywa się jednak na hoście i w zupełnie innym interfejsie
i musi pobierać te same informacje z systemu kompilacji.
W związku z tym CLC w czasie kompilacji używany przez dexpreopt i CLC w czasie wykonywania używany przez PackageManager
to ta sama wartość, ale obliczana na 2 różne sposoby.
CLC w czasie kompilacji i w czasie wykonywania muszą być takie same, w przeciwnym razie kod kompilowany w trybie AOT utworzony przez dexpreopt zostanie odrzucony. Aby sprawdzić równość czasu kompilacji
kompilatora dex2oat służącego do uruchamiania aplikacji CLC w czasie kompilacji w plikach *.odex
(w polu classpath
nagłówka OAT). Aby znaleźć zapisany plik CLC, użyj tego polecenia:
oatdump --oat-file=<FILE> | grep '^classpath = '
Niezgodność CLC w czasie kompilacji i w czasie działania jest zgłaszana w logcat podczas uruchamiania. Szukaj , używając tego polecenia:
logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch'
Niezgodność ma negatywny wpływ na wydajność, ponieważ zmusza bibliotekę lub aplikację do dexoptymalizacji albo do działania bez optymalizacji (np. kod aplikacji może wymagać wyodrębnienia w pamięci z pliku APK, co jest bardzo kosztowną operacją).
Biblioteka współużytkowana może być opcjonalna lub wymagana. Z punktu widzenia dexpreopt wymagana biblioteka musi być obecna w momencie kompilacji (jej brak powoduje błąd kompilacji). Opcjonalna biblioteka może być obecna lub nieobecna w momencie kompilacji: jeśli jest obecna, jest dodawana do CLC, przekazywana do dex2oat i rejestrowana w pliku *.odex
. Jeśli opcjonalna biblioteka jest nieobecna, jest pomijana i nie jest dodawana do biblioteki wspólnej. Jeśli stan biblioteki na etapie kompilacji i w czasie wykonywania się nie zgadzają (w jednym przypadku biblioteka opcjonalna jest obecna, a w drugim nie), CLC na etapie kompilacji i w czasie wykonywania się nie zgadzają i skompilowany kod zostaje odrzucony.
Zaawansowane szczegóły systemu kompilacji (poprawiarka manifestu)
Czasami w źródłowym pliku manifestu brakuje tagów <uses-library>
z biblioteki lub aplikacji. Może się tak na przykład zdarzyć, gdy jedna z zależności pośrednich
biblioteki lub aplikacji zacznie używać innego tagu <uses-library>
, a tag
biblioteka lub plik manifestu aplikacji nie jest aktualizowany.
Soong może obliczyć niektóre brakujące tagi <uses-library>
w danej bibliotece
lub aplikacja automatycznie, jako biblioteki pakietu SDK w przypadku zamknięcia zależności pośredniej
w bibliotece lub aplikacji. Jest to konieczne, ponieważ biblioteka (lub aplikacja) może
bazują na bibliotece statycznej, która jest zależna od biblioteki SDK, i może
i przechodnie
przez inną bibliotekę.
Nie wszystkie tagi <uses-library>
można obliczyć w ten sposób, ale jeśli to możliwe, lepiej pozwolić Soong na automatyczne dodawanie wpisów w pliku manifestu. Dzięki temu zmniejszysz liczbę błędów i uproszczając konserwację. Jeśli na przykład wiele aplikacji używa statycznej biblioteki, która dodaje nową zależność <uses-library>
, wszystkie aplikacje muszą zostać zaktualizowane, co jest trudne do utrzymania.