Określanie, które zasoby mają być przechowywane

Gdy włączasz optymalizację aplikacji, ustawienie isShrinkResources = true instruuje optymalizator, aby usuwał nieużywane zasoby, co pomaga zmniejszyć rozmiar aplikacji. Zmniejszenie rozmiaru zasobów działa tylko w połączeniu ze zmniejszaniem kodu, więc jeśli optymalizujesz zasoby, ustaw też isMinifyEnabled = true, na przykład:

buildTypes {
    release {
        isMinifyEnabled = true
        isShrinkResources = true
        ...
    }
}

Jeśli chcesz zachować lub odrzucić określone zasoby, utwórz plik XML keep w zasobach projektu, np. res/raw/my.package.keep.xml. Plik keep zawiera te komponenty:

  • Tag <resources> – zawiera wszystkie elementy zasobu podrzędnego i atrybuty keep/discard.
  • Atrybut tools:keep – akceptuje listę nazw zasobów oddzielonych przecinkami, która identyfikuje zasoby do zachowania.
  • Atrybut tools:discard – akceptuje rozdzieloną przecinkami listę nazw zasobów, które mają zostać odrzucone.

Użyj gwiazdki jako symbolu wieloznacznego, aby odwoływać się do wielu zasobów w tym samym folderze, 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" />

Określanie zasobów do odrzucenia może wydawać się zbędne, skoro można je po prostu usunąć, ale odrzucenie zasobów może być przydatne podczas korzystania z wariantów kompilacji.

Kierowanie na konkretne wersje kompilacji

Aby usunąć zasoby tylko w niektórych wariantach kompilacji, umieść wszystkie zasoby w katalogu wspólnego projektu, a potem utwórz w katalogu zasobów danego wariantu osobny plik my.package.build.variant.keep.xml dla każdego wariantu kompilacji. W pliku keep ręcznie określ zasoby do usunięcia, gdy dany zasób wydaje się być używany w kodzie (i zatem nie jest usuwany przez shrinkera), ale wiesz, że nie będzie używany w danym wariancie kompilacji.

Usuwanie nieużywanych zasobów alternatywnych

Optymalizator usuwa tylko zasoby, do których nie odwołuje się kod aplikacji. Oznacza to, że nie usuwa zasobów alternatywnych w przypadku różnych konfiguracji urządzeń.

Aby usunąć alternatywne pliki zasobów, których aplikacja nie potrzebuje, użyj właściwości resConfigs w pliku Gradle na Androida build.gradle w pliku modułu aplikacji build.gradle.

Jeśli na przykład używasz biblioteki zawierającej zasoby językowe (np. Usług Google Play), Twoja aplikacja zawiera wszystkie przetłumaczone ciągi tekstowe dla wiadomości w tych bibliotekach, niezależnie od tego, czy reszta aplikacji jest przetłumaczona na te same języki. Aby zachować tylko te języki, które są oficjalnie obsługiwane przez aplikację, określ te języki za pomocą właściwości resConfigs. Wszystkie zasoby dla niewymienionych języków zostaną usunięte.

Z tych fragmentów kodu dowiesz się, jak ograniczyć zasoby językowe tylko do języka angielskiego i francuskiego:

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

lub

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

Gdy publikujesz aplikację w formacie pakietu aplikacji na Androida (AAB), domyślnie po jej zainstalowaniu pobierane są tylko języki skonfigurowane na urządzeniu użytkownika. Podobnie tylko zasoby pasujące do gęstości ekranu urządzenia i biblioteki natywne pasujące do ABI urządzenia są uwzględniane w pobieraniu. Więcej informacji znajdziesz w artykule Włączanie i wyłączanie typów plików APK z konfiguracją.

W przypadku starszych aplikacji publikowanych jako pliki APK (utworzonych przed sierpniem 2021 roku) możesz dostosować gęstość ekranu lub zasoby interfejsu ABI, aby uwzględnić je w pliku APK. Aby to zrobić, utwórz kilka plików APK przeznaczonych na różne konfiguracje urządzeń.

Unikaj konfliktów podczas łączenia zasobów

Domyślnie wtyczka Android Gradle (AGP) scala zasoby o identycznych nazwach, takie jak zasoby do rysowania o tej samej nazwie, które znajdują się w różnych folderach zasobów. To zachowanie nie jest kontrolowane przez właściwość shrinkResources i nie można go wyłączyć, ponieważ jest ono konieczne, aby uniknąć błędów, gdy wiele zasobów ma nazwę, do której odwołuje się kod.

Scalanie zasobów ma miejsce tylko wtedy, gdy co najmniej 2 pliki mają identyczną nazwę, typ i kwalifikator zasobu. AGP wybiera plik, który według niego jest najlepszy spośród duplikatów (na podstawie kolejności priorytetów opisanej poniżej) i przekazuje tylko ten zasób do AAPT na potrzeby dystrybucji w ostatecznym pliku artefaktu.

AGP wyszukuje zduplikowane zasoby w tych lokalizacjach:

  • Główne zasoby powiązane z głównym zbiorem źródeł, zwykle znajdujące się w folderze src/main/res/
  • nakładki wersji, pochodzące z typu kompilacji i wersji kompilacji;
  • Zależności projektu biblioteki

AGP scala zduplikowane zasoby w takim porządku priorytetów:

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

Jeśli na przykład w zasobach głównych i wersji kompilacji występuje duplikat zasobu, Gradle wybiera zasób w wersji kompilacji.

Jeśli w tym samym zbiorze źródeł występują identyczne zasoby, Gradle nie może ich scalić i wyświetla błąd scalania zasobów. Może się tak zdarzyć, jeśli zdefiniujesz wiele zbiorów źródeł we właściwości sourceSet pliku modułu build.gradle, np. jeśli zarówno src/main/res/, jak i src/main/res2/ zawierają te same zasoby.

Rozwiązywanie problemów ze zmniejszaniem zasobów

Gdy zmniejszysz zasoby, w oknie Kompiluj zobaczysz podsumowanie zasobów, które zostały usunięte z aplikacji. Aby wyświetlić szczegółowy tekst z Gradle, po lewej stronie okna kliknij Przełącz widok. Przykład:

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

Gradle tworzy też plik diagnostyczny o nazwie resources.txt w folderze <module-name>/build/outputs/mapping/release/ (tym samym, co pliki wyjściowe ProGuarda). Plik zawiera informacje o tym, które zasoby odwołują się do innych zasobów oraz które zasoby są używane lub usunięte.

Aby na przykład dowiedzieć się, dlaczego element @drawable/ic_plus_anim_016 nadal znajduje się w aplikacji, otwórz plik resources.txt i wyszukaj tę nazwę. Możesz zauważyć, że jest ona używana w innym zasobie:

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

Musisz teraz wiedzieć, dlaczego @drawable/add_schedule_fab_icon_anim jest dostępny. Jeśli przesuniesz się w hierarchii w górę, znajdziesz zasób w sekcji Dostępne zasoby na poziomie wyższym:resources.txt.

Oznacza to, że w kodzie dostępnym do sprawdzenia jest odwołanie do add_schedule_fab_icon_anim, czyli znaleziono w nim identyfikator R.drawable.

Jeśli nie używasz ścisłej kontroli, identyfikatory zasobów mogą zostać oznaczone jako dostępne, jeśli istnieją ciągi znaków stałych, które mogą być użyte do utworzenia nazw zasobów wczytywanych dynamicznie. W takim przypadku, jeśli w wyniku działania polecenia build wyszukasz nazwę zasobu, możesz zobaczyć komunikat podobny do tego:

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because its 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 on używany do dynamicznego ładowania danego zasobu, użyj atrybutu tools:discard w pliku keep, aby poinformować system kompilacji o usunięciu zasobu.