Aby włączyć optymalizację aplikacji, musisz używać bibliotek zgodnych z optymalizacją Androida. Jeśli biblioteka nie jest skonfigurowana pod kątem optymalizacji Androida (np. używa odbicia bez dołączania powiązanych reguł zachowania), może nie być odpowiednia dla 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 pomogą Ci w wyborze.
Ogólne wskazówki dotyczące wyboru bibliotek
Postępuj zgodnie z tymi wskazówkami, aby mieć pewność, że Twoje biblioteki są zgodne z optymalizacją aplikacji.
Preferuj generowanie kodu zamiast odbicia
Wybieraj biblioteki, które używają generowania kodu (codegen) zamiast odbicia. Dzięki generowaniu kodu optymalizator może określić, który kod jest faktycznie używany w czasie działania, a który można usunąć. Trudno stwierdzić, czy biblioteka używa generowania kodu czy odbicia, ale istnieją pewne oznaki – zapoznaj się ze wskazówkami.
Więcej informacji o generowaniu kodu i odbiciu znajdziesz w artykule Optymalizacja dla autorów bibliotek.
Sprawdzanie, czy używane jest odbicie (zaawansowane)
Możesz sprawdzić, czy biblioteka używa odbicia, analizując jej kod. Jeśli biblioteka używa odbicia, sprawdź, czy zawiera powiązane reguły zachowania. Biblioteka prawdopodobnie używa odbicia, jeśli:
- używa klas lub metod z pakietów
kotlin.reflectlubjava.lang.reflect; - używa funkcji
Class.forNamelubclassLoader.getClass; - odczytuje adnotacje w czasie działania, np. jeśli przechowuje wartość adnotacji
za pomocą
val value = myClass.getAnnotation()lubval value = myMethod.getAnnotation(), a następnie wykonuje jakąś operację navalue; wywołuje metody, używając nazwy metody jako ciągu znaków, jak w tym przykładzie:
// Calls the private `processData` API with reflection myObject.javaClass.getMethod("processData", DataType::class.java) ?.invoke(myObject, data)
Sprawdzanie problemów z optymalizacją
Rozważając użycie nowej biblioteki, przejrzyj narzędzie do śledzenia problemów i dyskusje online, aby sprawdzić, czy występują problemy związane z minimalizacją lub konfigurowaniem optymalizacji aplikacji. Jeśli takie problemy występują, poszukaj alternatywnwnych bibliotek. Pamiętaj o tych kwestiach:
- Biblioteki AndroidX i biblioteki takie jak Hilt dobrze współpracują z optymalizacją aplikacji, ponieważ w większości przypadków używają generowania kodu zamiast odbicia. Gdy używają odbicia, zapewniają minimalne reguły zachowania, aby zachować tylko potrzebny kod.
- Biblioteki serializacji często używają odbicia, aby uniknąć powtarzalnego kodu podczas tworzenia instancji lub serializacji obiektów. Zamiast podejść opartych na odbiciu (takich jak Gson w przypadku JSON) poszukaj bibliotek, które używają generowania kodu, aby uniknąć tych problemów, np. korzystając z serializacji Kotlin {:.external} lub Moshi z generowaniem kodu.
- Jeśli to możliwe, unikaj bibliotek, które zawierają reguły zachowania obejmujące cały pakiet. Reguły zachowania obejmujące cały pakiet mogą pomóc w rozwiązaniu błędów, ale szerokie reguły zachowania należy ostatecznie doprecyzować, aby zachować tylko potrzebny kod. Więcej informacji znajdziesz w artykule Stopniowe wdrażanie optymalizacji.
- Przed opublikowaniem aplikacji, która używa biblioteki innej firmy, użyj analizatora konfiguracji R8, aby sprawdzić podane przez nią reguły zachowania. Przeglądając raport, możesz sprawdzić, czy reguły zachowania biblioteki nie są zbyt szerokie, co uniemożliwia R8 przeprowadzanie krytycznych optymalizacji w Twojej bazie kodu. Ta kontrola zapewnia, że wybrane biblioteki są zgodne z celami dotyczącymi wydajności aplikacji i nie powodują niepotrzebnego rozrostu konfiguracji.
- Biblioteki nie powinny wymagać kopiowania i wklejania reguł zachowania z dokumentacji do pliku w projekcie, zwłaszcza reguł zachowania obejmujących cały pakiet. Takie reguły stanowią długoterminowe obciążenie dla dewelopera aplikacji i trudno je optymalizować oraz zmieniać.
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 alternatywnych bibliotek lub napisz reguły zachowania. Jeśli biblioteka nie jest zgodna z optymalizacją, zgłoś błąd.
Filtrowanie nieprawidłowych reguł zachowania (zaawansowane)
Reguły zachowania się sumują. Oznacza to, że niektórych reguł zawartych w zależności biblioteki nie można usunąć i mogą one wpływać na kompilację innych części aplikacji. Jeśli na przykład biblioteka zawiera regułę wyłączającą optymalizację kodu, ta reguła wyłącza optymalizację całego projektu.
Unikaj bibliotek z regułami zachowania, które zachowują kod, który powinien zostać usunięty. Jeśli jednak musisz ich używać, 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 z optymalizacjami
Gson to biblioteka serializacji, która często powoduje problemy z optymalizacją aplikacji, ponieważ w dużym stopniu używa odbicia. Ten fragment kodu pokazuje, jak zwykle używa się biblioteki Gson, co może powodować awarie w czasie działania. Zwróć uwagę, że gdy używasz biblioteki Gson do pobierania listy obiektów User, nie wywołujesz konstruktora ani nie przekazujesz fabryki do funkcji fromJson(). Tworzenie lub używanie klas zdefiniowanych w aplikacji bez żadnego z tych elementów jest oznaką, że biblioteka może używać odbicia bez ograniczeń:
- klasa aplikacji implementująca bibliotekę lub standardowy interfejs albo klasa;
- 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()
Aby dowiedzieć się, jak R8 działa w przypadku biblioteki Gson, zapoznaj się z regułami konsumenta Gson. Gdy R8
analizuje ten kod i nie widzi nigdzie utworzonej instancji UserList ani User, może zmienić nazwy pól lub usunąć konstruktory, które nie wydają się być
używane, co spowoduje awarię aplikacji. Jeśli używasz innych bibliotek w podobny sposób, sprawdź, czy nie będą one zakłócać optymalizacji aplikacji, a jeśli tak, unikaj ich.
Aby zdefiniować klasy w sposób zgodny z regułami konsumenta Gson, użyj tego fragmentu kodu jako odniesienia:
class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)
Pamiętaj, że Room, Hilt i Moshi z generowaniem kodu tworzą typy zdefiniowane w aplikacji, ale używają generowania kodu, aby uniknąć konieczności używania odbicia.