Wybieranie bibliotek

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 powiązanych reguł zachowywania), 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

Dzięki tym wskazówkom możesz 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 jest kilka oznak, które mogą Ci w tym pomóc.

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 zachowywania. Biblioteka prawdopodobnie używa odbicia, jeśli:

  • używa klas lub metod z pakietów kotlin.reflect lub java.lang.reflect;
  • używa funkcji Class.forName lub classLoader.getClass;
  • odczytuje adnotacje w czasie działania, np. jeśli przechowuje wartość adnotacji za pomocą val value = myClass.getAnnotation() lub val value = myMethod.getAnnotation(), a następnie wykonuje jakąś operację na value;
  • 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 nie ma problemów związanych 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 zachowywania, 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ścia opartego na odbiciu (np. Gson w przypadku JSON) poszukaj bibliotek, które używają generowania kodu, aby uniknąć tych problemów, np. korzystając z serializacji Kotlin lub Moshi z generowaniem kodu.
  • Jeśli to możliwe, unikaj bibliotek, które zawierają reguły zachowywania obejmujące cały pakiet. Reguły zachowywania obejmujące cały pakiet mogą pomóc w rozwiązywaniu błędów, ale szerokie reguły zachowywania należy ostatecznie doprecyzować, aby zachować tylko potrzebny kod. Więcej informacji znajdziesz w artykule Stopniowe wprowadzanie optymalizacji.
  • Przed opublikowaniem aplikacji, która używa biblioteki innej firmy, użyj analizatora konfiguracji R8 do sprawdzenia podanych reguł zachowywania. Przeglądając raport, możesz sprawdzić, czy reguły zachowywania biblioteki nie są zbyt szerokie, co uniemożliwia R8 przeprowadzanie krytycznych optymalizacji w Twojej bazie kodu. To sprawdzenie gwarantuje, ż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ł zachowywania z dokumentacji do pliku w projekcie, zwłaszcza reguł zachowywania 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 zachowywania. Jeśli biblioteka nie jest zgodna z optymalizacją, zgłoś błąd.

Filtrowanie nieprawidłowych reguł zachowywania (zaawansowane)

Reguły zachowywania 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 zachowywania, 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 powoduje problemy z optymalizacją

Gson to biblioteka serializacji, która często powoduje problemy z optymalizacją aplikacji, ponieważ w dużym stopniu korzysta z 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, że UserList lub User są gdziekolwiek tworzone , 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.

Używanie @SerializedName w nowszych wersjach biblioteki Gson

Aby zdefiniować modele danych w sposób zgodny z regułami konsumenta Gson, dodaj adnotację @SerializedName do pól, jak pokazano w tym fragmencie kodu:

import com.google.gson.annotations.SerializedName

class User(@SerializedName("name") val name: String)
class UserList(@SerializedName("users") val users: List<User>)

Używając adnotacji @SerializedName, umożliwiasz R8 dopasowanie klas modelu do reguł zachowywania zawartych w bibliotece Gson w wersji 2.11.0 i nowszej. R8 automatycznie zachowuje pola z adnotacjami i niezbędne konstruktory, co pozwala korzystać z reguł zawartych w bibliotece bez konieczności ręcznego konfigurowania ProGuarda w projekcie.

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.