Optymalizacja dla autorów bibliotek

Jako autor biblioteki musisz zadbać o to, aby deweloperzy aplikacji mogli łatwo włączyć ją do swoich aplikacji, zachowując przy tym wysoką jakość wrażeń użytkowników. Upewnij się, że Twoja biblioteka jest zgodna z optymalizacją na Androida bez dodatkowej konfiguracji lub zgłoś, że może nie nadawać się do użycia na Androidzie.

Ta dokumentacja jest przeznaczona dla deweloperów opublikowanych bibliotek, ale może być też przydatna deweloperom modułów wewnętrznych bibliotek w dużej, skompilowanej aplikacji.

Jeśli jesteś deweloperem i chcesz dowiedzieć się więcej o optymalizowaniu aplikacji na Androida, przeczytaj artykuł Włączanie optymalizacji aplikacji. Aby dowiedzieć się, których bibliotek używać, przeczytaj artykuł Wybór odpowiednich bibliotek.

Używanie codegen zamiast reflection

Jeśli to możliwe, zamiast refleksji używaj generowania kodu (codegen). Zarówno generowanie kodu, jak i odwołania są popularnymi sposobami na unikanie szablonowego kodu podczas programowania, ale generowanie kodu jest bardziej zgodne z optymalizatorami aplikacji, takimi jak R8:

  • W przypadku codegen kod jest analizowany i modyfikowany podczas procesu kompilacji. Ponieważ po kompilacji nie ma żadnych istotnych modyfikacji, optymalizator wie, jaki kod jest potrzebny i co można bezpiecznie usunąć.
  • W przypadku odbicia kod jest analizowany i modyfikowany w czasie wykonywania. Ponieważ kod nie jest w pełni gotowy, dopóki się nie wykona, optymalizator nie wie, jaki kod można bezpiecznie usunąć. Prawdopodobnie usunie kod używany dynamicznie za pomocą odbicia lustrzanego podczas działania, co powoduje awarie aplikacji u użytkowników.

Wiele nowoczesnych bibliotek zamiast refleksji używa generowania kodu. Zapoznaj się z KSP, czyli wspólnym punktem wejścia używanym przez Room, Dagger2 i wiele innych usług.

Kiedy odbicie jest dopuszczalne

Jeśli musisz użyć odbicia, możesz to zrobić tylko w jednym z tych miejsc:

  • konkretne typy docelowe (określone implementacje interfejsu lub podklasy);
  • Kod korzystający z określonej adnotacji środowiska wykonawczego

Wykorzystanie odbicia w taki sposób ogranicza koszt działania i umożliwia tworzenie reguł dotyczących zachowania danych użytkownika.

Ta konkretna i wybiórcza forma odbicia jest wzorcem, który można zaobserwować zarówno w ramach platformy Android (np. podczas napełniania aktywności, widoków i elementów rysowalnych), jak i bibliotek AndroidX (np. podczas tworzenia obiektów WorkManager ListenableWorker lub RoomDatabases). Natomiast otwarta implementacja Gson nie jest odpowiednia do stosowania w aplikacjach na Androida.

Napisać reguły dotyczące zachowania konsumentów

Biblioteki powinny pakować reguły przechowywania „konsumenta”, które używają tego samego formatu co reguły przechowywania aplikacji. Te reguły są umieszczane w elementach biblioteki (AAR lub JAR) i automatycznie wykorzystywane podczas optymalizacji aplikacji na Androida, gdy biblioteka jest używana.

Biblioteki AAR

Aby dodać reguły dla konsumentów biblioteki AAR, użyj opcji consumerProguardFiles w skrypcie kompilacji modułu biblioteki na Androida. Więcej informacji znajdziesz w przewodniku po tworzeniu modułów biblioteki.

Kotlin

android {
    defaultConfig {
        consumerProguardFiles("consumer-proguard-rules.pro")
    }
    ...
}

Groovy

android {
    defaultConfig {
        consumerProguardFiles 'consumer-proguard-rules.pro'
    }
    ...
}

Biblioteki JAR

Aby zgrupować reguły z biblioteką Kotlin/Java dostarczaną jako plik JAR, umieść plik reguł w katalogu META-INF/proguard/ końcowego pliku JAR, pod dowolną nazwą. Jeśli na przykład Twój kod znajduje się w pliku <libraryroot>/src/main/kotlin, umieść plik reguł dla klienta w pliku <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro. Reguły zostaną zapakowane we właściwym miejscu w pliku JAR.

Sprawdź, czy końcowy plik JAR zawiera prawidłowe reguły, sprawdzając, czy znajdują się one w katalogu META-INF/proguard.

Optymalizacja kompilacji biblioteki AAR (zaawansowane)

Ogólnie rzecz biorąc, nie należy optymalizować kompilacji biblioteki bezpośrednio, ponieważ możliwe optymalizacje w czasie kompilacji biblioteki są bardzo ograniczone. Tylko podczas kompilowania aplikacji, gdy biblioteka jest dołączona do aplikacji, R8 może wiedzieć, jak są używane wszystkie metody biblioteki i które parametry są przekazywane. Jako twórca biblioteki musisz wziąć pod uwagę wiele etapów optymalizacji i zachować zachowanie zarówno w bibliotece, jak i w czasie kompilacji aplikacji, zanim zoptymalizujesz bibliotekę.

Jeśli nadal chcesz optymalizować bibliotekę w czasie kompilacji, możesz to zrobić za pomocą wtyczki Androida do obsługi Gradle.

Kotlin

android {
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
        configureEach {
            consumerProguardFiles("consumer-rules.pro")
        }
    }
}

Groovy

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                'proguard-rules.pro'
        }
        configureEach {
            consumerProguardFiles "consumer-rules.pro"
        }
    }
}

Pamiętaj, że działanie funkcji proguardFiles różni się od działania funkcji consumerProguardFiles:

  • proguardFiles są używane w czasie kompilacji, często razem z getDefaultProguardFile("proguard-android-optimize.txt"), aby określić, która część biblioteki powinna zostać zachowana podczas kompilacji biblioteki. To co najmniej publiczny interfejs API.
  • consumerProguardFiles są natomiast pakowane w bibliotece, aby wpływać na optymalizacje przeprowadzane później, podczas kompilacji aplikacji, która korzysta z Twojej biblioteki.

Jeśli np. Twoja biblioteka używa odbicia lustrzanego do tworzenia klas wewnętrznych, musisz zdefiniować reguły zachowania zarówno w proguardFiles, jak i consumerProguardFiles.

Jeśli w kompilacji biblioteki używasz -repackageclasses, przepakuj klasy do podpakietu wewnątrz pakietu biblioteki. Na przykład użyj -repackageclasses 'com.example.mylibrary.internal' zamiast -repackageclasses 'internal'.

Obsługa różnych wersji R8 (zaawansowane)

Możesz dostosować reguły do kierowania na konkretne wersje R8. Dzięki temu Twoja biblioteka będzie działać optymalnie w projektach korzystających z nowszych wersji R8, a obecne reguły będą mogły być nadal używane w projektach ze starszymi wersjami R8.

Aby określić kierowane reguły R8, musisz je umieścić w katalogu META-INF/com.android.tools w katalogu classes.jar pliku AAR lub w katalogu META-INF/com.android.tools pliku JAR.

In an AAR library:
    proguard.txt (legacy location, the file name must be "proguard.txt")
    classes.jar
    └── META-INF
        └── com.android.tools (location of targeted R8 rules)
            ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
            └── ... (more directories with the same name format)

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rule-files> (legacy location)
    └── com.android.tools (location of targeted R8 rules)
        ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
        └── ... (more directories with the same name format)

W katalogu META-INF/com.android.tools może być wiele podkatalogów o nazwach w formacie r8-from-<X>-upto-<Y>, które wskazują, dla których wersji R8 zostały napisane reguły. Każdy podkatalog może zawierać jeden lub więcej plików z zasadami R8 o dowolnych nazwach i rozszerzeniach.

Pamiętaj, że części -from-<X>-upto-<Y> są opcjonalne, wersja <Y> jest wyłączna, a zakresy wersji są zwykle ciągłe, ale mogą też się na siebie nakładać.

Na przykład r8, r8-upto-8.0.0, r8-from-8.0.0-upto-8.2.0r8-from-8.2.0 to nazwy katalogów, które reprezentują zestaw kierowanych reguł R8. Reguł w katalogu r8 można używać w dowolnej wersji R8. Zasady w katalogu r8-from-8.0.0-upto-8.2.0 mogą być używane przez R8 od wersji 8.0.0 do 8.2.0 z wyjątkiem tej ostatniej.

Wtyczka Androida Gradle używa tych informacji do wybrania wszystkich reguł, których można używać w bieżącej wersji R8. Jeśli biblioteka nie określa reguł R8, wtyczka Gradle na Androida wybierze reguły z pobocznych lokalizacji (proguard.txt w przypadku pliku AAR lub META-INF/proguard/<ProGuard-rule-files> w przypadku pliku JAR).