Aby włączyć optymalizację aplikacji, musisz używać bibliotek zgodnych z optymalizacją na Androida. Jeśli biblioteka nie jest skonfigurowana pod kątem optymalizacji na Androida (np. używa odzwierciedlenia bez powiązania z odpowiednimi regułami Keep), może nie nadawać się do stosowania w aplikacji na Androida. Na tej stronie wyjaśniamy, dlaczego niektóre biblioteki lepiej nadają się do optymalizacji aplikacji i podajemy ogólne wskazówki, które ułatwią Ci wybór.
preferować codegen zamiast refleksji;
Zazwyczaj należy wybierać biblioteki, które zamiast refleksji używają generowania kodu (codegen). Dzięki codegen optymalizator może łatwiej określić, jaki kod jest faktycznie używany w czasie wykonywania, a który można usunąć. Trudno jest stwierdzić, czy dana biblioteka korzysta z generowania kodu lub odbicia lustrzanego, ale istnieją pewne oznaki. Więcej informacji znajdziesz w wskazówkach.
Więcej informacji o porównywaniu kodu generowanego automatycznie z odwoływaniem znajdziesz w artykule Optymalizacja dla autorów bibliotek.
Ogólne wskazówki dotyczące wyboru bibliotek
Skorzystaj z tych wskazówek, aby mieć pewność, że Twoje biblioteki są zgodne z optymalizacją aplikacji.
Sprawdzanie problemów z optymalizacją
Jeśli rozważasz użycie nowej biblioteki, sprawdź, czy w jej śledzeniu problemów i dyskusjach online nie ma problemów związanych z skompresowaniem lub konfigurowaniem optymalizacji aplikacji. Jeśli tak, poszukaj alternatywnej biblioteki. Pamiętaj o tym:
- Biblioteki AndroidX i biblioteki takie jak Hilt dobrze sprawdzają się w optymalizacji aplikacji, ponieważ używają generowania kodu z poziomu kodu źródłowego zamiast refleksji. Gdy używają odbicia, określają minimalne reguły przechowywania, aby zachować tylko potrzebny kod.
- Biblioteki serializacji często używają odbicia, aby uniknąć szablonowego kodu podczas tworzenia i serializacji obiektów. Zamiast metod opartych na refleksji (takich jak Gson w przypadku JSON) poszukaj bibliotek, które korzystają z generowania kodu, aby uniknąć tych problemów. Możesz na przykład użyć Kotlin Serialization.
- Jeśli to możliwe, należy unikać bibliotek, które zawierają reguły keep dotyczące całego pakietu. Reguły zachowania dotyczące całego pakietu mogą pomóc w rozwiązywaniu błędów, ale ostatecznie należy je dostosować tak, aby zachowywały tylko niezbędny kod. Więcej informacji znajdziesz w artykule Postępowa optymalizacja.
Włączanie optymalizacji po dodaniu nowej biblioteki
Po dodaniu nowej biblioteki włącz optymalizację i sprawdź, czy nie ma błędów. Jeśli występują błędy, poszukaj alternatywnej biblioteki lub napisz reguły. Jeśli biblioteka nie jest zgodna z optymalizacją, zgłoś błąd tej biblioteki.
Reguły się sumują
Pamiętaj, że reguły zachowania się sumują. Oznacza to, że niektórych reguł uwzględnionych przez zależność od biblioteki nie można usunąć, ponieważ mogą one wpływać na kompilację innych części aplikacji. Jeśli na przykład biblioteka zawiera regułę wyłączającą optymalizacje kodu, reguła ta wyłącza optymalizacje dla całego projektu.
Sprawdzanie użycia odbicia (zaawansowane)
Możesz sprawdzić, czy biblioteka korzysta z odzwierciedlenia, analizując jej kod. Jeśli biblioteka korzysta z odzwierciedlenia, sprawdź, czy zawiera powiązane reguły Keep. Biblioteka prawdopodobnie korzysta z odbicia, jeśli:
- Używa klas lub metod z pakietów
kotlin.reflect
lubjava.lang.reflect
. - Używa funkcji
Class.forName
lubclassLoader.getClass
. - Czyta adnotacje w czasie wykonywania, np. gdy przechowuje wartość adnotacji za pomocą funkcji
val value = myClass.getAnnotation()
lubval value = myMethod.getAnnotation()
, a potem wykonuje jakąś operację za pomocą funkcjivalue
wywołuje metody, używając nazwy metody jako ciągu znaków, na przykład:
// Calls the private `processData` API with reflection myObject.javaClass.getMethod("processData", DataType::class.java) ?.invoke(myObject, data)
Filtrowanie nieprawidłowych reguł przechowywania (zaawansowane)
Unikaj bibliotek z regułami, które zachowują kod, który powinien zostać usunięty. Jeśli jednak musisz ich użyć, możesz odfiltrować reguły, jak pokazano w tym kodzie:
// If you're using AGP 8.4 and higher
buildTypes {
release {
optimization.keepRules {
it.ignoreFrom("com.somelibrary:somelibrary")
}
}
}
// If you're using AGP 7.3-8.3
buildTypes {
release {
optimization.keepRules {
it.ignoreExternalDependencies("com.somelibrary:somelibrary")
}
}
}
Studium przypadku: dlaczego Gson nie działa poprawnie po optymalizacji
Gson to biblioteka serializacji, która często powoduje problemy z optymalizacją aplikacji, ponieważ intensywnie korzysta z odzwierciedlenia. Poniższy fragment kodu pokazuje typowe użycie Gson, które może łatwo spowodować awarię w czasie wykonywania. Pamiętaj, że gdy używasz Gson do pobierania listy obiektów User, nie wywołujesz konstruktora ani nie przekazujesz fabryki do funkcji fromJson()
. Tworzenie lub używanie klas zdefiniowanych przez aplikację bez użycia któregoś z tych elementów jest sygnałem, że biblioteka może używać otwartego mechanizmu odzwierciedlenia:
- Klasa aplikacji implementująca bibliotekę lub standardowy interfejs lub klasę
- Wtyczka do generowania kodu, np. KSP
class User(val name: String)
class UserList(val users: List<User>)
// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()
Gdy R8 analizuje ten kod i nie widzi nigdzie instancji UserList
ani User
, może zmienić nazwy pól lub usunąć konstruktory, które nie są używane, co spowoduje awarię aplikacji. Jeśli używasz innych bibliotek w podobny sposób, sprawdź, czy nie zakłócają one optymalizacji aplikacji. Jeśli tak, nie używaj ich.
Pamiętaj, że Room i Hilt tworzą typy zdefiniowane przez aplikację, ale używają codegen, aby uniknąć konieczności użycia refleksji.