Wiadomości o usługach
Konfigurowanie reguł zachowywania R8 i rozwiązywanie związanych z nimi problemów
Czas czytania: 7 minut
W nowoczesnym programowaniu na Androida udostępnianie małej, szybkiej i bezpiecznej aplikacji jest podstawowym oczekiwaniem użytkowników. Głównym narzędziem systemu kompilacji Androida do osiągnięcia tego celu jest optymalizator R8 , czyli kompilator, który obsługuje usuwanie nieużywanego kodu i zasobów w celu zmniejszenia rozmiaru aplikacji, zmiany nazw kodu lub minifikacji oraz optymalizacji aplikacji.
Włączenie R8 jest kluczowym krokiem w przygotowaniu aplikacji do publikacji, ale wymaga od deweloperów podania wskazówek w postaci „zasad zachowania”.
Po przeczytaniu tego artykułu obejrzyj film Performance Spotlight Week w YouTube, w którym omawiamy włączanie, debugowanie i rozwiązywanie problemów z optymalizatorem R8.
Dlaczego potrzebne są reguły przechowywania
Konieczność pisania reguł Keep wynika z podstawowego konfliktu: R8 to narzędzie do analizy statycznej, ale aplikacje na Androida często opierają się na dynamicznych wzorcach wykonywania, takich jak odbicie lub wywołania w kodzie natywnym i z niego za pomocą JNI (Java Native Interface).
R8 tworzy wykres używanego kodu, analizując bezpośrednie wywołania. Gdy kod jest używany w dynamiczny sposób, analiza statyczna R8 nie może tego przewidzieć i oznacza go jako nieużywany, a następnie usuwa, co prowadzi do awarii w czasie działania.
Reguła zachowania to wyraźna instrukcja dla kompilatora R8, która mówi: „Ta konkretna klasa, metoda lub pole to punkt wejścia, do którego będzie się uzyskiwać dostęp dynamicznie w czasie działania programu. Musisz go zachować, nawet jeśli nie możesz znaleźć bezpośredniego odniesienia do niego”.
Więcej informacji o zasadach przechowywania znajdziesz w oficjalnym przewodniku.
Gdzie pisać reguły Keep
Niestandardowe reguły zachowywania dla aplikacji są zapisywane w pliku tekstowym. Zgodnie z konwencją ten plik ma nazwę proguard-rules.pro i znajduje się w katalogu głównym modułu aplikacji lub biblioteki. Ten plik jest następnie określany w rodzaju kompilacji release pliku build.gradle.kts modułu.
release {
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
Używanie prawidłowego pliku domyślnego
Metoda getDefaultProguardFile importuje domyślny zestaw reguł dostarczonych przez pakiet Android SDK. Jeśli użyjesz nieprawidłowego pliku, aplikacja może nie być zoptymalizowana. Pamiętaj, aby używać proguard-android-optimize.txt. Ten plik zawiera domyślne reguły zachowywania dla standardowych komponentów Androida i umożliwia optymalizację kodu przez R8. Przestarzałe proguard-android.txt udostępnia tylko reguły zachowywania, ale nie umożliwia optymalizacji R8.
Jest to poważny problem z wydajnością, dlatego zaczynamy ostrzegać deweloperów przed używaniem nieprawidłowego pliku. Pierwsze ostrzeżenia pojawią się w Android Studio Narwhal 3 z pakietem nowych funkcji. Od wersji 9.0 wtyczki Androida do obsługi Gradle nie obsługujemy już przestarzałego pliku proguard-android.txt. Dlatego pamiętaj, aby przejść na zoptymalizowaną wersję.
Jak pisać reguły Keep
Reguła przechowywania składa się z 3 głównych części:
-
Opcja , np.
-keeplub-keepclassmembers -
Opcjonalne modyfikatory, np.
allowshrinking. - specyfikacja klasy, która określa kod do dopasowania;
Pełną składnię i przykłady znajdziesz w instrukcji dodawania reguł przechowywania.
Antywzorce reguł Keep
Warto znać sprawdzone metody, ale też antyprzykłady. Te antypatie często wynikają z nieporozumień lub skrótów w rozwiązywaniu problemów i mogą mieć katastrofalny wpływ na wydajność kompilacji produkcyjnej.
Opcje globalne
Te flagi to globalne przełączniki, których nigdy nie należy używać w kompilacji do publikacji. Służą one tylko do tymczasowego debugowania w celu wyodrębnienia problemu.
Użycie -dontotptimize skutecznie wyłącza optymalizacje wydajności R8, co powoduje spowolnienie działania aplikacji.
Użycie -dontobfuscate wyłącza wszystkie zmiany nazw, a użycie -dontshrink wyłącza usuwanie martwego kodu. Obie te reguły globalne zwiększają rozmiar aplikacji.
Aby zapewnić użytkownikom aplikacji większą wydajność, unikaj używania tych globalnych flag w środowisku produkcyjnym, gdy tylko jest to możliwe.
Zbyt ogólne reguły przechowywania
Najłatwiejszym sposobem na zniwelowanie korzyści z R8 jest napisanie zbyt ogólnych reguł przechowywania. Reguły takie jak ta poniżej instruują optymalizator R8, aby nie zmniejszał, nie zaciemniał ani nie optymalizował żadnej klasy w tym pakiecie ani żadnego z jego podpakietów. Spowoduje to całkowite usunięcie korzyści z R8 w przypadku całego pakietu. Spróbuj utworzyć wąskie i konkretne reguły Keep.
-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMS
Operator negacji (!)
Operator negacji (!) wydaje się skutecznym sposobem na wykluczenie pakietu z reguły. Ale to nie takie proste. Weźmy ten przykład:
-keep class !com.example.my_package.** { *; } // USE WITH CAUTION
Możesz pomyśleć, że ta reguła oznacza „nie zachowuj klas wcom.example.package”. W rzeczywistości oznacza ona jednak „zachowaj każdą klasę, metodę i właściwość w całej aplikacji, która nie znajduje się w com.example.package”. Jeśli to Cię zaskoczyło, sprawdź, czy w konfiguracji R8 nie ma żadnych negacji.
Nadmiarowe reguły dotyczące komponentów Androida
Kolejnym częstym błędem jest ręczne dodawanie reguł Keep dla Activities, Services lub BroadcastReceivers w aplikacji. Jest to niepotrzebne. Domyślny plik proguard-android-optimize.txt zawiera już odpowiednie reguły, które umożliwiają działanie tych standardowych komponentów Androida od razu po wyjęciu z pudełka.
Wiele bibliotek ma też własne reguły przechowywania. Nie musisz więc pisać własnych reguł. Jeśli wystąpi problem z zasadami Keep w używanej bibliotece, najlepiej skontaktować się z jej autorem, aby dowiedzieć się, na czym polega problem.
Sprawdzone metody dotyczące reguł Keep
Wiesz już, czego nie robić, więc teraz porozmawiajmy o sprawdzonych metodach.
Tworzenie wąskich reguł przechowywania
Dobre reguły przechowywania powinny być jak najbardziej szczegółowe i precyzyjne. Powinny zachowywać tylko to, co jest niezbędne, aby R8 mógł zoptymalizować wszystko inne.
| Reguła | Jakość |
|---|---|
| Niski:zachowuje cały pakiet i jego podpakiet. |
| Niski: zachowuje całą klasę, która prawdopodobnie jest nadal zbyt szeroka. |
| Wysoki: zachowywane są tylko odpowiednie metody i właściwości z określonej klasy. |
Korzystanie ze wspólnych przodków
Zamiast pisać oddzielne reguły Keep dla wielu różnych modeli danych, napisz jedną regułę, która będzie dotyczyć wspólnej klasy bazowej lub interfejsu. Poniższa reguła nakazuje R8 zachowanie wszystkich elementów klas, które implementują ten interfejs, i jest wysoce skalowalna.
# Keep all fields of any class that implements SerializableModel
-keepclassmembers class * implements com.example.models.SerializableModel {
<fields>;
}
Używanie adnotacji do kierowania na wiele zajęć
Utwórz niestandardową adnotację (np. @Serialize) i użyj jej do „tagowania” klas, których pola mają zostać zachowane. Jest to kolejny przejrzysty, deklaratywny i wysoce skalowalny wzorzec. Możesz też tworzyć reguły Keep dla istniejących już adnotacji z używanych przez Ciebie platform.
# Keep all fields of any class annotated with @Serialize
-keepclassmembers class * {
@com.example.annotations.Serialize <fields>;
}
Wybieranie odpowiedniej opcji Keep
Opcja zachowania jest najważniejszą częścią reguły. Wybór nieprawidłowej opcji może niepotrzebnie wyłączyć optymalizację.
| Opcja zachowania | Jak działa |
-keep | Zapobiega usunięciu lub zmianie nazwy klasy i elementów wymienionych w deklaracji . |
-keepclassmembers | Uniemożliwia usunięcie lub zmianę nazwy określonych członków, ale umożliwia usunięcie samych zajęć, o ile nie zostały one usunięte w inny sposób. |
-keepclasseswithmembers | Połączenie: zachowuje klasę i jej członków tylko wtedy, gdy są obecni wszyscy określeni członkowie. |
Więcej informacji o opcji zachowywania znajdziesz w naszej dokumentacji dotyczącej opcji zachowywania.
Zezwalaj na optymalizację za pomocą modyfikatorów
Modyfikatory takie jak allowshrinking i allowobfuscation łagodzą ogólną regułę -keep, przywracając R8 możliwość optymalizacji. Jeśli np. starsza biblioteka wymusza użycie adnotacji -keep w całej klasie, możesz odzyskać część optymalizacji, zezwalając na zmniejszanie i zaciemnianie:
# Keep this class, but allow R8 to remove it if it's unused and allow R8 to rename it. -keep,allowshrinking,allowobfuscation class com.example.LegacyClass
Dodawanie opcji globalnych na potrzeby dodatkowej optymalizacji
Oprócz reguł zachowywania możesz dodać do pliku konfiguracyjnego R8 flagi globalne, aby jeszcze bardziej zwiększyć optymalizację.
-repackageclasses to zaawansowana opcja, która nakazuje R8 przeniesienie wszystkich zaciemnionych klas do jednego pakietu. Pozwala to zaoszczędzić dużo miejsca w pliku DEX dzięki usunięciu zbędnych ciągów znaków z nazwami pakietów.
-allowaccessmodification umożliwia R8 rozszerzenie dostępu (np. z private na public), aby umożliwić bardziej agresywne wstawianie kodu. Jest ona teraz domyślnie włączona podczas korzystania z proguard-android-optimize.txt.
Ostrzeżenie: autorzy bibliotek nigdy nie powinni dodawać tych globalnych flag optymalizacji do reguł konsumenta, ponieważ byłyby one stosowane do całej aplikacji.
Aby jeszcze bardziej to wyjaśnić, w wersji 9.0 wtyczki Androida do obsługi Gradle zaczniemy całkowicie ignorować globalne flagi optymalizacji z bibliotek.
Sprawdzone metody dotyczące bibliotek
Każda aplikacja na Androida w mniejszym lub większym stopniu korzysta z bibliotek. Omówmy więc sprawdzone metody dotyczące bibliotek.
Dla deweloperów bibliotek
Jeśli Twoja biblioteka korzysta z odbicia lub JNI, musisz udostępnić niezbędne reguły Keep jej użytkownikom. Te reguły są umieszczane w pliku consumer-rules.pro, który jest następnie automatycznie dołączany do pliku AAR biblioteki.
android {
defaultConfig {
consumerProguardFiles("consumer-rules.pro")
}
...
}
Dla użytkowników biblioteki
Odfiltrowywanie problematycznych reguł Keep
Jeśli musisz użyć biblioteki, która zawiera problematyczne reguły Keep, możesz je odfiltrować w pliku build.gradle.kts, począwszy od AGP 9.0. Dzięki temu R8 będzie ignorować reguły pochodzące z określonej zależności.
release {
optimization.keepRules {
// Ignore all consumer rules from this specific library
it.ignoreFrom("com.somelibrary:somelibrary")
}
}
Najlepsza reguła przechowywania to brak reguły przechowywania
Najlepsza strategia konfiguracji R8 polega na całkowitym wyeliminowaniu konieczności pisania reguł Keep. W przypadku wielu aplikacji można to osiągnąć, wybierając nowoczesne biblioteki, które preferują generowanie kodu zamiast odbicia.Dzięki generowaniu kodu optymalizator może łatwiej określić, który kod jest faktycznie używany w czasie działania programu, a który można usunąć. Brak dynamicznego odbicia oznacza też brak „ukrytych” punktów wejścia, a co za tym idzie, nie są potrzebne żadne reguły Keep Rules. Wybierając nową bibliotekę, zawsze preferuj rozwiązanie, które wykorzystuje generowanie kodu zamiast odbicia.
Więcej informacji o wybieraniu bibliotek znajdziesz w artykule Wybieraj biblioteki z rozsądkiem.
Debugowanie i rozwiązywanie problemów z konfiguracją R8
Jeśli R8 usunie kod, który powinien zachować, lub Twój plik APK jest większy niż oczekiwano, użyj tych narzędzi, aby zdiagnozować problem.
Znajdowanie zduplikowanych i globalnych reguł przechowywania
R8 łączy reguły z dziesiątek źródeł, więc trudno określić „ostateczny” zestaw reguł. Dodanie tego flagi do pliku proguard-rules.pro generuje pełny raport:
# Outputs the final, merged set of rules to the specified file -printconfiguration build/outputs/logs/configuration.txt
Możesz przeszukać ten plik, aby znaleźć zbędne reguły lub prześledzić problematyczną regułę (np. -dontoptimize) i odnaleźć konkretną bibliotekę, która ją zawiera.
Ask R8: Why are you keeping this?
Jeśli zajęcia, które miały zostać usunięte, nadal są widoczne w aplikacji, R8 może podać tego przyczynę. Wystarczy dodać tę regułę:
# Asks R8 to explain why it's keeping a specific class class com.example.MyUnusedClass -whyareyoukeeping
Podczas kompilacji R8 wyświetli dokładny łańcuch odwołań, które spowodowały zachowanie tej klasy, co pozwoli Ci prześledzić odwołanie i dostosować reguły.
Pełny przewodnik znajdziesz w sekcji Rozwiązywanie problemów z R8.
Dalsze kroki
R8 to zaawansowane narzędzie do zwiększania wydajności aplikacji na Androida. Jego skuteczność zależy od prawidłowego zrozumienia jego działania jako silnika analizy statycznej.
Tworząc szczegółowe reguły na poziomie członka, korzystając z przodków i adnotacji oraz starannie wybierając odpowiednie opcje przechowywania, możesz zachować dokładnie to, co jest potrzebne. Najbardziej zaawansowanym rozwiązaniem jest całkowite wyeliminowanie potrzeby stosowania reguł przez wybieranie nowoczesnych bibliotek opartych na generowaniu kodu zamiast ich poprzedników opartych na odbiciu.
W ramach Tygodnia poświęconego skuteczności obejrzyj dzisiejszy film na YouTube i kontynuuj wyzwanie R8. W przypadku pytań dotyczących włączania R8 lub rozwiązywania problemów z nim używaj tagu #optimizationEnabled. Chętnie Ci pomożemy.
Czas przekonać się o korzyściach.
Zachęcamy do włączenia pełnego trybu R8 w swojej aplikacji już dziś.
- Aby rozpocząć, zapoznaj się z naszymi przewodnikami dla deweloperów: Włącz optymalizację aplikacji.
-
Sprawdź, czy nadal używasz
proguard-android.txt, i zastąp go symbolemproguard-android-optimize.txt. - Następnie zbadaj wpływ. Nie tylko poczuj różnicę, ale też ją sprawdź. Aby zmierzyć wzrost wydajności, dostosuj kod z naszej przykładowej aplikacji do testów porównawczych na GitHubie i zmierz czasy uruchamiania przed i po wprowadzeniu zmian.
Jesteśmy przekonani, że zauważysz znaczną poprawę wyników aplikacji.
Przy okazji użyj tagu społecznościowego #AskAndroid, aby zadać pytania. Nasi eksperci przez cały tydzień monitorują i odpowiadają na Twoje pytania.
Jutro opowiemy o optymalizacji z użyciem profili, w tym profili podstawowych i startowych, pokażemy, jak w ostatnich wersjach poprawiła się wydajność renderowania w Compose, i omówimy kwestie związane z wydajnością pracy w tle.
Czytaj dalej
-
Wiadomości o usługach
Android Studio Panda 4 jest już stabilny i możesz go używać w środowisku produkcyjnym. Wprowadziliśmy m.in. tryb planowania i przewidywanie kolejnych zmian, dzięki czemu tworzenie wysokiej jakości aplikacji na Androida jest jeszcze łatwiejsze.
Matt Dyor • Czas czytania: 5 minut
-
Wiadomości o usługach
Jeśli jesteś deweloperem aplikacji na Androida i chcesz wdrożyć w niej innowacyjne funkcje oparte na AI, niedawno udostępniliśmy nowe, zaawansowane aktualizacje.
Thomas Ezan • Czas czytania: 3 minuty
-
Wiadomości o usługach
Android 17 osiągnął etap wersji beta 4, czyli ostatniej zaplanowanej wersji beta w tym cyklu wydawniczym, co jest kluczowym kamieniem milowym dla zgodności aplikacji i stabilności platformy.
Daniel Galpin • Czas czytania: 4 minuty
Bądź na bieżąco
Otrzymuj co tydzień najnowsze informacje o tworzeniu aplikacji na Androida na swoją skrzynkę odbiorczą.