Zmniejszanie, zaciemnianie i optymalizowanie aplikacji

Aby aplikacja była jak najmniejsza i szybka, zoptymalizuj ją i zmniejsz kompilację do publikacji w isMinifyEnabled = true.

Spowoduje to zmniejszanie, co skutkuje usunięciem nieużywanego kodu i zasobów. zaciemnianie kodu, które skraca nazwy klas i członków grupy w aplikacji; oraz optymalizację, która stosuje bardziej agresywne strategie, by jeszcze bardziej ograniczyć rozmiar aplikacji i zwiększanie jej wydajności. Na tej stronie opisujemy, jak R8 które pozwalają wykonać te zadania w czasie kompilacji w projekcie i jak dostosować .

Gdy tworzysz projekt za pomocą wtyczka Androida do obsługi Gradle w wersji 3.4.0 lub nowszej, wtyczka nie używa już ProGuard do optymalizacji kodu w czasie kompilowania. Zamiast tego wtyczka współpracuje z kompilatorem R8, aby obsługiwać: zadania w czasie kompilacji:

  • Zmniejszanie kodu (lub drżenie drzew): wykrywa i bezpiecznie usuwa nieużywane klasy, pola, metody i atrybuty z aplikacji i jej biblioteki zależności (co jest cennym narzędziem do pracy wokół 64 tys. plików referencyjnych). Na przykład, jeśli używasz tylko kilka interfejsów API zależności biblioteki, zmniejszenie może zidentyfikować kod biblioteki że aplikacja nie używa, i usuń tylko ten kod z aplikacji. Do więcej informacji znajdziesz w sekcji poświęconej zmniejszaniu kodu.
  • Zmniejszanie zasobów: usuwa nieużywane zasoby z aplikacji w pakiecie. włącznie z nieużywanymi zasobami w zależnościach biblioteki aplikacji. Działa w w połączeniu z kurczeniem kodu, tak że po usunięciu nieużywanego kodu wszystkie zasoby, do których już się nie odwołują, możesz bezpiecznie usunąć. Aby się uczyć Więcej informacji znajdziesz w sekcji zmniejszyć zasoby.
  • Optymalizacja: sprawdza i przeredaguje kod, aby poprawić czas działania. i jeszcze bardziej zmniejsz rozmiar plików DEX aplikacji. Ten poprawia wydajność kodu w czasie działania kodu nawet o 30%, znacząco ulepszając czas uruchamiania i czasu renderowania klatek. Jeśli na przykład R8 wykryje, że else {} gałąź danej instrukcji if/else nie jest wykonywana, R8 usuwa kod dla gałąź else {}. Więcej informacji znajdziesz w sekcji optymalizacji kodu.
  • Zaciemnianie (lub minifikacja identyfikatora): skraca nazwę klas. i członków grupy, co zmniejsza rozmiar plików DEX. Aby dowiedzieć się więcej, wejdź na sekcji o zaciemnianiu kodu.

Podczas tworzenia wersji produkcyjnej aplikacji R8 można skonfigurować tak, omówionych powyżej zadań kompilacyjnych. Możesz też wyłączyć określone lub dostosować działanie R8 za pomocą plików reguł ProGuard. R8 działa ze wszystkimi istniejącymi plikami reguł ProGuard, zaktualizowanie wtyczki Androida do obsługi Gradle tak, aby używała R8, nie powinno wymagać zmiany istniejących reguł.

Włącz zmniejszanie, zaciemnianie i optymalizację

Jeśli używasz Androida Studio 3.4 lub wtyczki Android do obsługi Gradle w wersji 3.4.0 lub nowszej, R8 jest domyślny kompilator, który konwertuje kod bajtowy Java projektu na format DEX działający na platformie Androida. Jednak podczas tworzenia nowego projektu w Android Studio, zmniejszanie, zaciemnianie i optymalizacja kodu nie są domyślnie włączone. To dlatego, że optymalizacje podczas kompilowania zwiększają czas na stworzenie projektu. Mogą też spowodować błędy, jeśli nie będziesz dostosować kod, który chcesz zachować.

Dlatego podczas tworzenia ostatecznej wersji pliku najlepiej jest włączyć te zadania kompilacyjne. którą przetestujesz przed opublikowaniem. Aby włączyć zmniejszanie, zaciemnianie, i optymalizację, umieść w skrypcie kompilacji na poziomie projektu te elementy.

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `isDebuggable=false`.
            isMinifyEnabled = true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            isShrinkResources = true

            proguardFiles(
                // Includes the default ProGuard rules files that are packaged with
                // the Android Gradle plugin. To learn more, go to the section about
                // R8 configuration files.
                getDefaultProguardFile("proguard-android-optimize.txt"),

                // Includes a local, custom Proguard rules file
                "proguard-rules.pro"
            )
        }
    }
    ...
}

Odlotowe

android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `debuggable false`.
            minifyEnabled true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            shrinkResources true

            // Includes the default ProGuard rules files that are packaged with
            // the Android Gradle plugin. To learn more, go to the section about
            // R8 configuration files.
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

Pliki konfiguracji R8

R8 używa plików z regułami ProGuard, aby modyfikować domyślne działanie i ulepszać poznawać strukturę aplikacji, np. klasy, które stanowią punkty wejścia; do kodu aplikacji. Chociaż można modyfikować niektóre z tych plików reguł, niektóre mogą być generowane automatycznie przez narzędzia czasu kompilacji, takie jak AAPT2, lub odziedziczone z zależności biblioteki aplikacji. Tabela poniżej zawiera źródeł plików reguł ProGuard, z których korzysta R8.

Źródło Lokalizacja Opis
Android Studio, <module-dir>/proguard-rules.pro Gdy tworzysz nowy moduł w Android Studio, IDE tworzy plik proguard-rules.pro w katalogu głównym tego modułu.

Domyślnie ten plik nie stosuje żadnych reguł. Dodaj więc własne Tutaj reguły ProGuard, takie jak niestandardowy zachowaj reguły.

Wtyczka Androida do obsługi Gradle Wygenerowany przez wtyczkę Androida do obsługi Gradle podczas kompilowania. Wtyczka Androida do obsługi Gradle generuje proguard-android-optimize.txt, która obejmuje reguły jest przydatny w większości projektów na Androida. @Keep* adnotacji.

Domyślnie podczas tworzenia nowego modułu w Android Studio moduł skrypt kompilacji zawiera ten plik reguł w kompilacji do publikacji. dla Ciebie.

Uwaga: wtyczka Androida do obsługi Gradle zawiera dodatkowe wstępnie zdefiniowane proGuard plików reguł, ale zalecamy proguard-android-optimize.txt

Zależności bibliotek

W bibliotece AAR:
proguard.txt

W bibliotece JAR:
META-INF/proguard/<ProGuard-rules-file>

Oprócz tych lokalizacji wtyczka Androida do obsługi Gradle w wersji 3.6 lub nowszej obsługuje kierowane reguły ograniczania.

Jeśli biblioteka AAR lub JAR została opublikowana z własnym plikiem reguł i uwzględnić tę bibliotekę jako zależność czasu kompilowania, R8 automatycznie stosuje te reguły podczas kompilowania projektu.

Oprócz konwencjonalnych reguł ProGuard wtyczka Androida do obsługi Gradle 3.6 lub nowsza wersja obsługuje też docelowych reguł ograniczania. To są reguły kierowane na konkretne urządzenia kurczące (R8 lub ProGuard) i pomniejszania.

Używanie plików reguł spakowanych z bibliotekami jest przydatne, jeśli reguły są wymagane do prawidłowego funkcjonowania biblioteki. podał już instrukcje rozwiązywania problemów.

Pamiętaj jednak, że reguły są dodatkowe, nie można usunąć pewnych reguł ujętych w zależności bibliotecznej, może wpłynąć na kompilację innych części aplikacji. Jeśli na przykład plik biblioteka zawiera regułę wyłączania optymalizacji kodu, która wyłącza tę regułę i optymalizacji całego projektu.

Android Asset Package Tool 2 (AAPT2) Po utworzeniu projektu w minifyEnabled true: <module-dir>/build/intermediates/aapt_proguard_file/.../aapt_rules.txt AAPT2 generuje reguły zachowywania na podstawie odwołań do zajęć w aplikacji pliku manifestu, układów i innych zasobów aplikacji. Na przykład AAPT2 zawiera element zachowuj regułę dla każdej aktywności zarejestrowanej w pliku manifestu aplikacji jako i punktu wejścia.
Niestandardowe pliki konfiguracji Domyślnie, gdy tworzysz nowy moduł w Android Studio, IDE tworzy <module-dir>/proguard-rules.pro, aby umożliwić Ci dodanie własnych reguł. Możesz uwzględnić dodatkowe konfiguracje, a R8 stosuje je podczas kompilowania.

Jeśli ustawisz właściwość minifyEnabled na wartość true, R8 połączy reguły ze wszystkich wymienionych powyżej dostępnych źródeł. Należy pamiętać o tym, rozwiązywać problemy z modelem R8, ponieważ inne zależności czasu kompilowania takich jak zależności bibliotek, mogą wprowadzić zmiany w działaniu R8, których nie wiemy.

Aby wygenerować pełny raport ze wszystkimi regułami, których ma zastosowanie R8 podczas tworzenia projekt, umieść w pliku proguard-rules.pro modułu te informacje:

// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt

Docelowe reguły ograniczania

Wtyczka Androida do obsługi Gradle w wersji 3.6 lub nowszej obsługuje biblioteki reguły dotyczące kierowania konkretnych reduktorów (R8 lub ProGuard) i konkretnych wersji. Ten umożliwia programistom bibliotekom dostosowanie reguł do optymalnego działania w projektach które korzystają z nowych wersji zmniejszających rozmiar, przy czym umożliwia pozostawienie istniejących reguł używane w projektach ze starszymi wersjami kurczaka.

Aby określić docelowe reguły ograniczania, programiści bibliotek muszą je uwzględnić w określonych lokalizacjach w bibliotece AAR lub JAR, jak opisano poniżej.

In an AAR library:
    proguard.txt (legacy location)
    classes.jar
    └── META-INF
        └── com.android.tools (targeted shrink rules location)
            ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
            └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rules-file> (legacy location)
    └── com.android.tools (targeted shrink rules location)
        ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
        └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

Oznacza to, że docelowe reguły ograniczania są przechowywane w: META-INF/com.android.tools katalogu JAR lub katalogu META-INF/com.android.tools wewnątrz classes.jar ARA.

W tym katalogu może znajdować się wiele katalogów o nazwach w postaci r8-from-<X>-upto-<Y> lub proguard-from-<X>-upto-<Y>, aby wskazać, dla których są zapisywane reguły wewnątrz katalogów. Pamiętaj, że części -from-<X> i -upto-<Y> są opcjonalne. Wersja <Y> jest wyłącznie, a zakresy wersji muszą być ciągłe.

np. r8-upto-8.0.0, r8-from-8.0.0-upto-8.2.0 i r8-from-8.2.0. aby utworzyć prawidłowy zestaw docelowych reguł ograniczania. Zasady określone w Katalog r8-from-8.0.0-upto-8.2.0 będzie używany przez R8 od wersji 8.0.0 do ale bez wersji 8.2.0.

Biorąc pod uwagę te informacje, wtyczka Androida do obsługi Gradle w wersji 3.6 lub nowszej wybierze z pasujących katalogów R8. Jeśli biblioteka nie określa kierowania reguł ograniczania, wtyczka Androida do obsługi Gradle wybierze reguły ze starszej lokalizacji (proguard.txt w przypadku AAR lub META-INF/proguard/<ProGuard-rules-file> w przypadku pliku JAR).

Programiści bibliotek mogą uwzględnić docelowe reguły ograniczania lub starsze reguły ProGuard w swoich bibliotekach lub oba typy, jeśli chcą zachować zgodność z wtyczką Androida do obsługi Gradle w wersji starszej niż 3.6 lub innymi narzędziami.

Uwzględnij dodatkowe konfiguracje

Gdy tworzysz nowy projekt lub moduł w Android Studio, IDE tworzy plik <module-dir>/proguard-rules.pro z własnymi regułami. Ty może również zawierać dodatkowe reguły z innych plików, dodając je do polecenia proguardFiles w skrypcie kompilacji modułu.

Możesz na przykład dodać reguły, które są unikalne dla każdego wariantu kompilacji, dodając inną właściwość proguardFiles w odpowiednim bloku productFlavor. za to plik Gradle dodaje atrybut flavor2-rules.pro do rodzaju usług flavor2. Teraz flavor2 wykorzystuje wszystkie 3 reguły ProGuard, ponieważ te z zasady release także zostaną zastosowane.

Możesz też dodać właściwość testProguardFiles, która określa lista plików ProGuard, które znajdują się tylko w testowym pakiecie APK:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                "proguard-rules.pro"
            )
            testProguardFiles(
                // The proguard files listed here are included in the
                // test APK only.
                "test-proguard-rules.pro"
            )
        }
    }
    flavorDimensions.add("version")
    productFlavors {
        create("flavor1") {
            ...
        }
        create("flavor2") {
            proguardFile("flavor2-rules.pro")
        }
    }
}

Odlotowe

android {
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                'proguard-rules.pro'
            testProguardFiles
                // The proguard files listed here are included in the
                // test APK only.
                'test-proguard-rules.pro'
        }
    }
    flavorDimensions "version"
    productFlavors {
        flavor1 {
            ...
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

Zmniejszanie kodu

Zmniejszanie kodu za pomocą R8 jest domyślnie włączone po ustawieniu minifyEnabled usłudze na true.

Zmniejszanie kodu (nazywane też potrząsaniem drzew) to proces usuwania kodu. które według R8 nie są wymagane w czasie działania. Ten proces może znacznie ograniczyć rozmiaru aplikacji, jeśli na przykład aplikacja zawiera wiele zależności bibliotek, wykorzystuje tylko niewielką część swoich funkcji.

Aby skrócić kod aplikacji, R8 najpierw określa wszystkie punkty wejścia aplikacji na podstawie połączonego zestawu plików konfiguracji. Te punkty wejścia obejmują wszystkie klasy, których platforma Android może używać do otwierania Aktywność lub usługi w aplikacji. R8 sprawdza od każdego punktu wejścia kodu aplikacji, by utworzyć wykres wszystkich metod, zmiennych członkowskich i innych klas, do których aplikacja może mieć dostęp w czasie działania. Kod, który nie jest połączony z ten wykres jest uważany za nieosiągalny i można go usunąć z aplikacji.

Rysunek 1 przedstawia aplikację, która jest uzależniona od biblioteki środowiska wykonawczego. Podczas sprawdzania kodu aplikacji, R8 wskazuje, że metody foo(), faz() i bar() są jest osiągalny z punktu wejścia MainActivity.class. Jednak klasa Aplikacja OkayApi.class ani jej metoda baz() nigdy nie są używane przez aplikację w czasie działania oraz R8 usuwa ten kod po zmniejszeniu aplikacji.

Rysunek 1. Podczas kompilowania R8 tworzy wykres oparty na połączonych regułach przechowywania w projekcie, które pozwalają określić nieosiągalny kod.

R8 określa punkty wejścia za pomocą reguł -keep w projekcie Pliki konfiguracji R8. Oznacza to, że reguły przechowywania określają, klas, które nie powinny być odrzucane przez R8 przy zmniejszaniu aplikacji, a R8 uwzględnia jako punkty wejścia do aplikacji. Wtyczka Androida do obsługi Gradle i AAPT2 automatycznie generują reguły przechowywania, które są wymagane przez większość aplikacji projekty, takie jak aktywność, widoki czy usługi w aplikacji. Pamiętaj jednak: Jeśli chcesz dostosować to domyślne działanie przy użyciu dodatkowych reguł zachowywania, przeczytaj sekcji o tym, jak dostosować kod do zachowania.

Jeśli zamiast tego chcesz zmniejszyć rozmiar zasobów aplikacji, przejdź do sekcji z informacjami o zmniejszaniu zasobów.

Pamiętaj, że po zmniejszeniu projektu biblioteki aplikacja zależna od tej biblioteki obejmuje zmniejszone klasy biblioteki. Może być konieczne dostosowanie reguł przechowywania w bibliotece, w pliku APK biblioteki brakuje klas. Jeśli tworzysz i publikujesz bibliotekę w formacie AAR, lokalne pliki JAR, od których Twoja biblioteka nie jest w pliku AAR.

Wybierz kod, który chcesz zachować

W większości przypadków domyślny plik reguł ProGuard (proguard-android-optimize.txt) wystarcza do usunięcia nieużywanego kodu przez R8. Pamiętaj jednak: R8 może mieć trudności z poprawną analizą i może spowodować, potrzebne aplikacji. Przykłady sytuacji, w których może zostać niesłusznie usunięte kod obejmuje:

  • Gdy aplikacja wywołuje metodę z interfejsu natywnego Java (JNI)
  • Gdy aplikacja wyszukuje kod w czasie działania (np. podczas odbicia)

Testowanie aplikacji powinno wykazać błędy spowodowane niewłaściwym usunięciem. ale możesz też zobaczyć, który kod został usunięty generując raport o usuniętym kodzie.

Aby naprawić błędy i wymusić zachowanie określonego kodu w R8, dodaj -keep w pliku reguł ProGuard. Na przykład:

-keep public class MyClass

Możesz też dodać parametr @Keep adnotację do kodu, którą chcesz zachować. Dodanie elementu @Keep do zajęć powoduje zachowanie całej klasy bez zmian. Dodanie ich do metody lub pola spowoduje również zachowanie metody/pola (oraz jego nazwy). . Pamiętaj, że ta adnotacja jest dostępna tylko wtedy, gdy używasz Biblioteka adnotacji AndroidX a gdy dołączysz plik z regułami ProGuard, który jest w pakiecie z Androidem wtyczki Gradle zgodnie z opisem w sekcji o włącz zmniejszanie.

Korzystając z opcji -keep, należy wziąć pod uwagę wiele kwestii. w przypadku więcej informacji o dostosowywaniu pliku reguł znajdziesz w Instrukcja ProGuard. Rozwiązywanie problemów by dowiedzieć się, jakie inne typowe problemy mogą wystąpić po

Usuń biblioteki natywne

Domyślnie biblioteki kodu natywnego są usuwane z kompilacji wersji aplikacji. Obejmuje to usunięcie tabeli symboli i danych debugowania zawarte we wszystkich bibliotekach natywnych używanych przez aplikację. Usuwanie kodu natywnego biblioteki pozwalają znacząco zmniejszyć rozmiar plików. jednak niemożliwe jest zdiagnozowanie awarii w Konsoli Google Play z powodu brakujących informacji (np. nazw klas i funkcji).

Obsługa natywnych awarii

Konsola Google Play zgłasza awarie natywne w kategorii Android Vitals. Kilka możesz wygenerować i przesłać plik symboli do debugowania kodu natywnego aplikacji. Ten plik umożliwia symbolizowane zrzuty stosu awarii (obejmujące klasy i nazwy funkcji) w Android Vitals, aby ułatwić debugowanie aplikacji w wersji produkcyjnej. Te kroki różnią się w zależności od wersji wtyczki Androida do obsługi Gradle używanej w oraz dane wyjściowe kompilacji projektu.

Wtyczka Androida do obsługi Gradle w wersji 4.1 lub nowszej

Jeśli w ramach projektu tworzysz pakiet Android App Bundle, możesz automatycznie dołączyć pliku symboli do debugowania kodu natywnego. Aby uwzględnić ten plik w kompilacjach wersji, dodaj parametr do pliku build.gradle.kts aplikacji:

android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }

Wybierz jeden z tych poziomów symboli debugowania:

  • Użyj funkcji SYMBOL_TABLE, aby uzyskać nazwy funkcji w symbolicznych zrzutach stosu w Konsoli Play. Ten poziom obsługuje tombstone.
  • Użyj FULL, aby uzyskać nazwy funkcji, pliki i numery wierszy w Konsoli Play symboliczne zrzuty stosu.
.

Jeśli Twój projekt tworzy plik APK, użyj przedstawionych ustawień kompilacji build.gradle.kts wcześniej, aby oddzielnie wygenerować plik symboli do debugowania kodu natywnego. ręcznie. prześlij plik symboli do debugowania kodu natywnego z Konsolą Google Play. W ramach procesu kompilacji narzędzie Gradle Androida wtyczka generuje ten plik w następującej lokalizacji projektu:

app/build/outputs/native-debug-symbols/variant-name/native-debug-symbols.zip

Wtyczka Androida do obsługi Gradle w wersji 4.0 lub starszej (i inne systemy kompilacji)

W ramach procesu kompilacji wtyczka Androida do obsługi Gradle przechowuje kopię pliku biblioteki w katalogu projektu. Struktura katalogów jest podobna do tej:

app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── arm64-v8a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── x86/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
└── x86_64/
    ├── libgameengine.so
    ├── libothercode.so
    └── libvideocodec.so
  1. Skompresuj zawartość tego katalogu:

    cd app/build/intermediates/cmake/universal/release/obj
    zip -r symbols.zip .
    
  2. ręcznie. prześlij plik symbols.zip z Konsolą Google Play.

.

Zmniejszanie zasobów

Zmniejszanie zasobów działa tylko w połączeniu ze zmniejszaniem kodu. Po kurczący kod usuwa cały nieużywany kod, zmniejszający zasoby może zidentyfikować zasobów, których nadal używa aplikacja. Dotyczy to zwłaszcza dodawania kodu bibliotek, które zawierają zasoby, należy usunąć nieużywany kod biblioteki, zasoby biblioteki stają się nieodwołane, więc zasób z kurczakiem.

Aby włączyć zmniejszanie zasobów, ustaw właściwość shrinkResources do true w skrypcie kompilacji (wraz minifyEnabled, aby zmniejszyć kod). Na przykład:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isShrinkResources = true
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

Odlotowe

android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android.txt'),
                'proguard-rules.pro'
        }
    }
}

Jeśli nie masz jeszcze aplikacji korzystającej z usługi minifyEnabled zmniejszanie kodu, a potem spróbuj to zrobić przed włączeniem funkcji shrinkResources, bo może być konieczne edytowanie pliku proguard-rules.pro na zachowuj klasy lub metody, które są tworzone lub wywoływane dynamicznie, zanim zacznij usuwać zasoby.

Wybierz zasoby, które chcesz zachować

Jeśli chcesz zachować lub odrzucić konkretne zasoby, utwórz plik XML. w projekcie, używając tagu <resources> i określ każdy z nich zasób do zachowania w atrybucie tools:keep i każdy zasób odrzuć w atrybucie tools:discard. Oba atrybuty akceptują rozdzielana przecinkami lista nazw zasobów. Gwiazdki możesz użyć jako symbol wieloznaczny.

Na przykład:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />

Zapisz ten plik w zasobach projektu, na przykład pod adresem res/raw/my.package.keep.xml Kompilacja nie spakuje tego pliku w .

Uwaga: nazwa pliku keep musi być unikalna. Kiedy gdy różne biblioteki zostaną połączone, ich reguły przechowywania będą sprzeczne powodując potencjalne problemy z ignorowanymi regułami lub zachowywane i zasobami Google Cloud.

Określanie zasobów do odrzucenia może wydawać się głupawe, zamiast je usuwać, ale może to być przydatne, gdy używasz wariantów kompilacji. Dla: możesz na przykład umieścić wszystkie zasoby we wspólnym katalogu projektów, a następnie utwórz osobny plik my.package.build.variant.keep.xml dla każdego z nich. wariant kompilacji, gdy wiesz, że dany zasób wydaje się być używany w kodzie (i nie zostanie usunięta przez usługę zmniejszająca), ale wiesz, że nie zostanie używany dla danego wariantu kompilacji. Narzędzia do kompilacji nieprawidłowo zidentyfikował zasób jako potrzebny, co może być spowodowane tym, kompilator dodaje identyfikatory zasobów w tekście, a analizator zasobów może nie rozpoznawanie różnicy między prawdziwie przywoływanym zasobem a wartością całkowitą w kodzie, które ma tę samą wartość.

Włącz rygorystyczne sprawdzanie odwołań

Normalnie narzędzie do obniżania zasobów może dokładnie określić, i zgody na określone zastosowania ich danych. Jeśli jednak kod nawiązuje połączenie z Resources.getIdentifier() (lub jeśli któraś z Twoich bibliotek tak działa, plik AppCompat ), co oznacza, że kod wyszukuje nazwy zasobów na podstawie dynamicznie generowanych ciągów znaków. Gdy to zrobisz, będzie działać zmniejszanie zasobów defensywny domyślnie i oznacza wszystkie zasoby z pasującym formatem jako potencjalnie używanych i niedostępnych do usunięcia.

Na przykład ten kod powoduje, że wszystkie zasoby z atrybutem Prefiks img_ do oznaczenia jako używany.

Kotlin

val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)

Java

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

Zmniejsza ono też wszystkie stałe łańcuchowe oraz różne zasoby res/raw/ w poszukiwaniu zasobów Adresy URL mają format podobny do file:///android_res/drawable//ic_plus_anim_016.png Jeśli znajdzie ciągi takie jak ten lub inne, które wyglądają, jakby można je było wykorzystać do tworzenia adresów URL nie zostaną usunięte.

Oto przykłady bezpiecznego pomniejszania, który jest domyślnie włączony. Możesz jednak wyłączyć to ustawienie. i określić aby narzędzie do ograniczania zasobów zatrzymywało tylko te zasoby, których użycie jest pewne. Do w tym celu ustaw shrinkMode na strict w keep.xml w ten sposób:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />

Jeśli włączysz tryb ścisłego ograniczania, a Twój kod odwołuje się do z dynamicznie generowanymi ciągami znaków, jak pokazano powyżej, musisz ręcznie zachować te zasoby za pomocą atrybutu tools:keep.

Usuń nieużywane zasoby alternatywne

Zmniejszający zasoby Gradle usuwa tylko zasoby, do których się nie odwołuje przez kod aplikacji, co oznacza, że nie usunie i zasobów alternatywnych dla różnych konfiguracji urządzeń. W razie potrzeby: możesz użyć właściwości resConfigs wtyczki Androida do obsługi Gradle, usuń pliki zasobów alternatywnych, których aplikacja nie potrzebuje.

Na przykład jeśli używasz biblioteki, która zawiera zasoby językowe (np. AppCompat lub Usługi Google Play), aplikacja zawiera wszystkie przetłumaczone ciągi językowe wiadomości w tych bibliotekach, niezależnie od tego, reszta aplikacji jest przetłumaczona na te same języki lub nie. Jeśli chcesz zachować tylko języki oficjalnie obsługiwane przez aplikację, możesz określić we właściwości resConfig. Wszystkie materiały dla Usunięto nieokreślone języki.

Z poniższego fragmentu dowiesz się, jak ograniczyć zasoby językowe do Angielski i francuski:

Kotlin

android {
    defaultConfig {
        ...
        resourceConfigurations.addAll(listOf("en", "fr"))
    }
}

Odlotowe

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

Przy publikowaniu aplikacji w formacie Android App Bundle domyślnie tylko języki skonfigurowane na urządzeniu użytkownika są pobierane podczas instalowania aplikacji. I podobnie, tylko zasoby pasujące do gęstości ekranu urządzenia i natywny biblioteki zgodne z interfejsem ABI urządzenia są uwzględniane w pobieranym pliku. Więcej zapoznaj się z Aplikacja na Androida Konfiguracja pakietu.

W przypadku starszych aplikacji publikowanych z plikami APK (utworzonych przed sierpniem 2021 r.) możesz: dostosować gęstość ekranu lub zasoby ABI, które należy uwzględnić w pliku APK tworzenie wielu plików APK, które są kierowane na inną konfigurację urządzenia.

Scal zduplikowane zasoby

Domyślnie Gradle scala też zasoby o identycznej nazwie, takie jak obiekty rysowane o tej samej nazwie, które mogą znajdować się w różnych folderach zasobów. Ten zachowanie nie zależy od właściwości shrinkResources i nie można wyłączyć, ponieważ konieczne jest uniknięcie błędów w przypadku wielu zasobów pasują do nazwy, którą wyszukuje Twój kod.

Scalanie zasobów ma miejsce tylko wtedy, gdy co najmniej 2 pliki są identyczne nazwę, typ i kwalifikator zasobu. Gradle wybiera plik, który bierze pod uwagę być najlepszą opcją (według priorytetu opisanego wg poniżej) i przekazuje tylko ten jeden zasób do AAPT do dystrybucji efekt końcowy.

Gradle szuka zduplikowanych zasobów w tych lokalizacjach:

  • Główne zasoby, powiązane z głównym zbiorem źródłowym, zwykle w lokalizacji: src/main/res/.
  • Nakładki wersji z typu kompilacji i jej smaków.
  • Zależności projektu biblioteki.

Gradle scala zduplikowane zasoby w tej kaskadowej kolejności według priorytetu:

Zależności → Główny → Rodzaj kompilacji → Typ kompilacji

Jeśli na przykład zduplikowany zasób pojawia się zarówno w zasobach głównych, rodzaj kompilacji, Gradle wybiera ten z rodzaju kompilacji.

Jeśli identyczne zasoby występują w tym samym zbiorze źródłowym, Gradle nie może scalić i powoduje wystąpienie błędu scalania zasobów. Może się tak zdarzyć, jeśli zdefiniujesz wiele zestawy źródeł we właściwości sourceSet na koncie build.gradle.kts – na przykład, jeśli zarówno src/main/res/, i src/main/res2/ zawierają identyczne zasoby.

Zaciemnianie kodu

Szyfrowanie ma na celu zmniejszenie rozmiaru aplikacji przez skrócenie nazw klasy, metody i pola aplikacji. Oto przykład: zaciemnianie kodu przy użyciu R8:

androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
    android.content.Context mContext -> a
    int mListItemLayout -> O
    int mViewSpacingRight -> l
    android.widget.Button mButtonNeutral -> w
    int mMultiChoiceItemLayout -> M
    boolean mShowTitle -> P
    int mViewSpacingLeft -> j
    int mButtonPanelSideLayout -> K

Zaciemnianie kodu nie usuwa kodu z aplikacji, ale znacznie zmniejsza rozmiar można zobaczyć w aplikacjach z plikami DEX, które indeksują wiele klas, metod i pól. Gdy jednak zaciemnianie kodu zmienia nazwy różnych części kodu, niektóre zadania takich jak sprawdzanie zrzutów stosu, wymagają dodatkowych narzędzi. Aby zrozumieć śledzenia stosu po zaciemnieniu kodu, przeczytaj sekcję o tym, dekodować zaciemniony zrzut stosu.

Dodatkowo, jeśli kod opiera się na przewidywalnych nazwach metod aplikacji i zajęć, na przykład używając refleksji, należy traktować te podpisów jako punktów wejścia i określ dla nich reguły zachowywania, tak jak to opisano w o tym, jak wybrać kod do zachowania. Te funkcje pozwalają zgodnie z regułami R8 nie tylko zachowa kod w ostatecznej wersji DEX aplikacji, jego oryginalnej nazwy.

Dekodowanie zaciemnionego zrzutu stosu

Gdy mechanizm R8 zaciemnia kod, trudno jest zrozumieć zrzut stosu (jeśli nie jest to możliwe), ponieważ nazwy klas i metod mogły zostać została zmieniona. Aby uzyskać oryginalny zrzut stosu, ponownie śledzić zrzut stosu.

Optymalizacja kodu

Aby jeszcze bardziej zoptymalizować aplikację, R8 dokładnie sprawdza kod poziomu konta, aby usunąć więcej nieużywanego kodu. Tam, gdzie to możliwe, przeredagować kod, i mniej szczegółowe. Oto kilka przykładów takich optymalizacji:

  • Jeśli Twój kod nie używa gałęzi else {} w danej instrukcji if/else, R8 może usunąć kod gałęzi else {}.
  • Jeśli Twój kod wywołuje metodę tylko w kilku miejscach, R8 może ją usunąć i umieścić go w kilku witrynach połączeń.
  • Jeśli R8 ustali, że klasa ma tylko jedną niepowtarzalną podklasę, a klasa to sam nie jest utworzony (na przykład abstrakcyjna klasa bazowa używana tylko przez konkretnej klasy implementacji), R8 może połączyć te dwie klasy usunąć je z aplikacji.
  • Aby dowiedzieć się więcej, przeczytaj Posty na blogu na temat optymalizacji R8 autorstwa Jake'a Whartona.

R8 nie umożliwia wyłączenia ani włączenia dyskretnych optymalizacji ani modyfikacji zachowanie optymalizacji. R8 ignoruje wszystkie reguły ProGuard, które próbują aby modyfikować domyślne optymalizacje, takie jak -optimizations czy -optimizationpasses To ograniczenie jest ważne, ponieważ gdy R8 nie przestaje tych ulepszeń, utrzymanie standardowego zachowania w optymalizacji pomaga Zespół Studio możesz łatwo rozwiązywać i rozwiązywać napotkane problemy.

Pamiętaj, że włączenie optymalizacji spowoduje zmianę zrzutów stosu dla aplikacji. Na przykład wbudowanie spowoduje usunięcie ramek stosu. Zapoznaj się z sekcją na temat: wycofywać, aby dowiedzieć się, jak uzyskać pierwotne zrzuty stosu.

Wpływ na wydajność środowiska wykonawczego

Jeśli włączysz zmniejszanie, zaciemnianie i optymalizowanie, R8 poprawi wydajność kodu w czasie działania (w tym czas uruchamiania i klatek w wątku UI) o 30%. Wyłączenie dowolnej z tych opcji znacznie ogranicza zestaw optymalizacji Zastosowania R8.

Jeśli włączona jest funkcja R8, należy też tworzenie profili startowych, co pozwoli Ci uzyskiwać jeszcze lepsze wyniki w przypadku startupów.

Włącz bardziej agresywne optymalizacje

R8 zawiera zestaw dodatkowych optymalizacji (nazywanych „trybem całego ruchu”), które sprawia, że działa inaczej niż ProGuard. Te optymalizacje są włączone przez domyślnie od Wtyczka Androida do obsługi Gradle w wersji 8.0.0.

Możesz wyłączyć te dodatkowe optymalizacje, uwzględniając w sekcji plik gradle.properties Twojego projektu:

android.enableR8.fullMode=false

Ze względu na dodatkowe optymalizacje R8 działa inaczej niż ProGuard, może wymagać dodania dodatkowych reguł ProGuard, aby uniknąć czasu działania jeśli używasz reguł przeznaczonych dla ProGuard. Na przykład załóżmy, że Twój odwołuje się do klasy za pomocą interfejsu Java Reflection API. Gdy nie używasz „tryb całego ruchu”, R8 zakłada, że zamierzasz badać obiekty i manipulować nimi w momencie działania tej klasy – nawet jeśli Twój kod faktycznie tego nie robi – i automatycznie zachowuje klasę i jej inicjator statyczny.

Jednak w „trybie całego ruchu” R8 nie zakłada tego założenia, a w przypadku R8 twierdzi, że w przeciwnym razie kod nigdy nie używa klasy w czasie działania, usuwa z końcowej wersji pliku DEX aplikacji. Oznacza to, że jeśli chcesz zachować zajęcia i swoje inicjator statyczny, musisz uwzględnić regułę zachowywania w pliku reguł że.

W razie problemów podczas korzystania z „trybu pełnego” R8 zapoznaj się z R8 – strona z najczęstszymi pytaniami . Jeśli nie możesz rozwiązać problemu, zgłoś błąd.

Wycofuję zrzuty stosu

Kod przetwarzany przez R8 jest zmieniany na różne sposoby, co może powodować zrzuty stosu trudniejsze do zrozumienia, ponieważ zrzuty stosu nie będą dokładnie odpowiadać kodowi źródłowemu. Ten może dotyczyć zmian w numerach wierszy, jeśli informacje debugowania nie zachowywano. Może to być spowodowane optymalizowaniem, takim jak wstęp i konspekt. co jest największym czynnikiem zaciemniania kodu, ponieważ nawet klasy i metody zmieniać nazwy.

Aby przywrócić pierwotny zrzut stosu, R8 udostępnia funkcje narzędzia wiersza poleceń retrace, które jest w pakiecie z pakietem narzędzi wiersza poleceń.

Aby umożliwić cofanie zrzutów stosu aplikacji, upewnij się, zachowuje wystarczającą ilość informacji do ponownego użycia, dodając do pliku proguard-rules.pro modułu:

-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

Atrybut LineNumberTable zachowuje informacje o pozycji w metodach, które umożliwiają wydrukowanie tych pozycji w zrzutach stosu. Atrybut SourceFile daje pewność, że we wszystkich potencjalnych środowiskach wykonawczych wydrukowane są informacje o pozycji. Dyrektywa -renamesourcefileattribute ustawia nazwę pliku źródłowego w stosie ślady tylko do SourceFile. Rzeczywista nazwa oryginalnego pliku źródłowego nie jest wymagane przy wycofywaniu, ponieważ plik mapowania zawiera oryginalny plik źródłowy.

Przy każdym uruchomieniu R8 tworzy plik mapping.txt, który zawiera informacje potrzebne do zmapowania zrzutów stosu z powrotem do oryginału zrzuty stosu. Android Studio zapisze plik w <module-name>/build/outputs/mapping/<build-type>/ katalogu.

Podczas publikowania aplikacji w Google Play możesz przesłać plik mapping.txt dla każdej wersji aplikacji. W przypadku publikowania za pomocą pakietów Android App Bundle jest automatycznie dołączany do treści pakietu aplikacji. Następnie Google Google Play będzie ponownie śledzić przychodzące zrzuty stosu w przypadku problemów zgłoszonych przez użytkowników, możesz je sprawdzić w Konsoli Play. Więcej informacji znajdziesz w Centrum pomocy artykuł o tym, usuwać zaciemnienie kodu w zrzucie stosu awarii.

Rozwiązywanie problemów przy użyciu R8

W tej sekcji omawiamy strategie rozwiązywania problemów z kurczenia, zaciemniania i optymalizacji przy użyciu R8. Jeśli nie znajdziesz rozwiązania zapoznaj się z poniższymi informacjami R8 – strona z najczęstszymi pytaniami oraz Przewodnik rozwiązywania problemów ProGuard.

Generowanie raportu o usuniętym (lub zachowanym) kodzie

Aby pomóc w rozwiązaniu niektórych problemów z R8, warto wyświetlić raport cały kod usunięty z aplikacji przez R8. Dla każdego modułu, który ma być aby wygenerować ten raport, dodaj -printusage <output-dir>/usage.txt do listy niestandardowej z regułami. Gdy włączysz R8 i stworzysz aplikację, R8 wygeneruje błąd raportu o określonej ścieżce i nazwie pliku. Zgłoszenie usuniętego kodu wygląda podobnie do tego:

androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
    public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
    public boolean hasWindowFeature(int)
    public void setHandleNativeActionModesEnabled(boolean)
    android.view.ViewGroup getSubDecor()
    public void setLocalNightMode(int)
    final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
    public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
    private static final boolean DEBUG
    private static final java.lang.String KEY_LOCAL_NIGHT_MODE
    static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
...

Jeśli zamiast tego chcesz zobaczyć raport punktów wejścia określany przez R8 przestrzegaj reguł przechowywania w swoim projekcie , uwzględnij -printseeds <output-dir>/seeds.txt w . Gdy włączysz R8 i stworzysz aplikację, R8 wygeneruje raport o podanej ścieżce i nazwie pliku. Raport dotyczący zachowanych wpisów punktów wygląda podobnie do tego:

com.example.myapplication.MainActivity
androidx.appcompat.R$layout: int abc_action_menu_item_layout
androidx.appcompat.R$attr: int activityChooserViewStyle
androidx.appcompat.R$styleable: int MenuItem_android_id
androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
androidx.lifecycle.FullLifecycleObserverAdapter
...

Rozwiązywanie problemów ze zmniejszaniem zasobów

Gdy zmniejszasz zasoby, kompilacja pokazuje podsumowanie zasobów usuniętych z aplikacji. (Najpierw kliknij Przełącz widok, po lewej stronie okna, aby wyświetlić szczegółowy tekst wygenerowany przez Gradle). Na przykład:

:android:shrinkDebugResources
Removed unused resources: Resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

Gradle tworzy też też plik diagnostyczny o nazwie resources.txt w <module-name>/build/outputs/mapping/release/ (ten sam jako pliki wyjściowe ProGuard). Plik zawiera m.in. informacje o tym, zasoby odwołują się do innych zasobów i wskazują, które zasoby są używane usunięto.

Aby na przykład dowiedzieć się, dlaczego @drawable/ic_plus_anim_016 to wciąż w aplikacji, otwórz plik resources.txt i wyszukaj go nazwę pliku. Możesz zauważyć, że odwołuje się do niego inny zasób, następujące:

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

Już wiesz, dlaczego @drawable/add_schedule_fab_icon_anim jest osiągalny. Jeśli wyszukasz w górę, zobaczysz, że zasób jest wymieniony w sekcji „Osiągalne zasoby główne to:”. Oznacza to, że istnieje odwołanie do kodu do add_schedule_fab_icon_anim (czyli jego identyfikator R.drawable to w osiągniętym kodzie).

Jeśli nie używasz ścisłego sprawdzania, identyfikatory zasobów mogą być oznaczane jako osiągalne jeśli istnieją stałe ciągi znaków, które wyglądają, jakby można je było wykorzystać do nazwy zasobów ładowanych dynamicznie. W takim przypadku, jeśli wyszukasz w wynikach kompilacji dla nazwy zasobu możesz zobaczyć taki komunikat:

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because it format-string matches string pool constant ic_plus_anim_%1$d.

Jeśli widzisz jeden z tych ciągów i masz pewność, że nie jest to jest używany do dynamicznego wczytywania danego zasobu, możesz użyć funkcji tools:discard, aby poinformować system kompilacji o jego usunięciu, Jak opisano w sekcji dotyczącej dostosowywania zasobów do zachowania.