Jako autor biblioteki musisz zadbać o to, aby deweloperzy aplikacji mogli łatwo ją włączyć 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ąć.
- Dzięki odbiciu 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 (konkretni implementatorzy 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ć w ramach zarówno 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 RoomDatabase). Natomiast otwarta implementacja Gson nie jest odpowiednia do stosowania w aplikacjach na Androida.
Napisać reguły dotyczące zachowania konsumenta
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 artykule o 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, upewniając się, że znajdują się one w katalogu META-INF/proguard
.
Optymalizacja kompilacji biblioteki AAR (zaawansowane)
Ogólnie nie należy optymalizować bezpośrednio biblioteki utworzonej z poziomu biblioteki, ponieważ możliwe optymalizacje w czasie tworzenia 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 momencie 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 zgetDefaultProguardFile("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 na przykład 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 narzędzi do kompresji (zaawansowane);
Możesz dostosować reguły do konkretnych kompresorów (R8 lub ProGuard), a także do konkretnych wersji kompresorów. Dzięki temu Twoja biblioteka będzie działać optymalnie w projektach korzystających z nowych wersji shrinkera, a obecne reguły będą mogły być nadal używane w projektach z starszymi wersjami shrinkera.
Aby określić reguły kompresji docelowej, musisz je uwzględnić w określonych miejscach w bibliotece AAR lub JAR, jak opisano poniżej.
In an AAR library:
consumer-proguard-rules.pro (legacy location)
classes.jar
└── META-INF
└── com.android.tools (targeted shrink rules location)
├── r8-from-<X>-upto-<Y>/<R8-rules-file>
└── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>
In a JAR library:
META-INF
├── proguard/<ProGuard-rules-file> (legacy location)
└── com.android.tools (targeted shrink rules location)
├── r8-from-<X>-upto-<Y>/<R8-rules-file>
└── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>
Oznacza to, że reguły spakowania docelowego są przechowywane w katalogu META-INF/com.android.tools
pliku JAR lub w katalogu META-INF/com.android.tools
w pliku classes.jar
pliku AAR.
W tym katalogu może być wiele katalogów o nazwach w formacie r8-from-<X>-upto-<Y>
lub proguard-from-<X>-upto-<Y>
, które wskazują, dla których wersji shrinkera zostały napisane reguły w katalogach.
Pamiętaj, że części -from-<X>
i -upto-<Y>
są opcjonalne, wersja <Y>
jest wyłączna, a zakresy wersji muszą być ciągłe.
Na przykład r8-upto-8.0.0, r8-from-8.0.0-upto-8.2.0
i r8-from-8.2.0
tworzą prawidłowy zbiór reguł skurczu docelowego. Reguły w katalogu r8-from-8.0.0-upto-8.2.0
będą używane przez R8 od wersji 8.0.0 do z wyjątkiem wersji 8.2.0.
Na podstawie tych informacji wtyczka Android Gradle wybiera reguły z odpowiednich katalogów R8. Jeśli biblioteka nie określa reguł kompresji, wtyczka Gradle na Androida wybierze reguły z starszych lokalizacji (proguard.txt
w przypadku pliku AAR lub META-INF/proguard/<ProGuard-rules-file>
w przypadku pliku JAR).