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 na Androida, np. używa odbicia bez dołączania 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.

Wybieranie generowania kodu zamiast odbicia

Zwykle należy wybierać biblioteki, które zamiast odbicia używają generowania kodu (codegen). Dzięki generowaniu kodu optymalizator może łatwiej 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 – pomocne mogą być wskazówki.

Więcej informacji o generowaniu kodu w porównaniu z odbiciem 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ą

Zanim zdecydujesz się na 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 tak, poszukaj alternatywnych bibliotek. Pamiętaj o tych kwestiach:

  • Biblioteki AndroidX i biblioteki takie jak Hilt dobrze współpracują z optymalizacją aplikacji, ponieważ używają generowania kodu zamiast odbicia. Gdy używają odbicia, podają 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ść opartych na odbiciu (np. Gson w przypadku JSON) poszukaj bibliotek, które używają generowania kodu, aby uniknąć tych problemów, np. Kotlin Serialization.
  • Jeśli to możliwe, unikaj bibliotek zawierających reguły zachowywania dotyczące całego pakietu. Reguły zachowywania dotyczące całego pakietu mogą pomóc w rozwiązaniu błędów, ale ogólne reguły zachowywania należy ostatecznie doprecyzować, aby zachować tylko potrzebny kod. Więcej informacji znajdziesz w artykule Stopniowe wprowadzanie optymalizacji.
  • Biblioteki nie powinny wymagać kopiowania i wklejania reguł przechowywania z dokumentacji do pliku w projekcie, zwłaszcza reguł przechowywania dotyczących całego pakietu. W dłuższej perspektywie reguły te stanowią obciążenie dla dewelopera aplikacji, a ich optymalizacja i zmiana z czasem są trudne.

Włączanie optymalizacji po dodaniu nowej biblioteki

Po dodaniu nowej biblioteki włącz optymalizację i sprawdź, czy nie występują błędy. 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 dotyczący tej biblioteki.

Reguły się sumują

Pamiętaj, że reguły przechowywania 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ą optymalizacje kodu, wyłącza ona optymalizacje w całym projekcie.

Sprawdzanie, czy nie jest używane odbicie (zaawansowane)

Możesz sprawdzić, czy biblioteka używa odbicia, analizując jej kod. Jeśli biblioteka korzysta z odbicia, sprawdź, czy udostępnia powiązane reguły zachowywania. Biblioteka prawdopodobnie korzysta z odbicia, jeśli:

  • Korzysta z 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 działanie z value.
  • Wywołuje metody, używając nazwy metody jako ciągu znaków, np.:

    // 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 zachowywania, które zatrzymują 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 z optymalizacjami

Gson to biblioteka serializacji, która często powoduje problemy z optymalizacją aplikacji, ponieważ w dużym stopniu wykorzystuje odbicie. Poniższy fragment kodu pokazuje, jak zwykle używa się biblioteki Gson, co może łatwo spowodować awarię 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 zdefiniowanych przez aplikację klas bez spełnienia jednego z tych warunków wskazuje, że biblioteka może używać otwartego odbicia:

  • Klasa aplikacji implementująca bibliotekę, 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 wydają się używane, co może spowodować awarię aplikacji. Jeśli używasz innych bibliotek w podobny sposób, sprawdź, czy nie będą one zakłócać optymalizacji aplikacji. Jeśli tak się stanie, unikaj ich.

Pamiętaj, że zarówno Room, jak i Hilt tworzą typy zdefiniowane przez aplikację, ale używają generowania kodu, aby uniknąć konieczności odbicia.