Jako autor biblioteki musisz zadbać o to, aby deweloperzy aplikacji mogli łatwo włączyć ją do swoich aplikacji, zachowując jednocześnie wysoką jakość wrażeń użytkowników. Upewnij się, że Twoja biblioteka jest zgodna z optymalizacją Androida bez dodatkowej konfiguracji, lub udokumentuj, że może być nieodpowiednia do użycia na Androidzie.
Ta dokumentacja jest przeznaczona dla deweloperów opublikowanych bibliotek, ale może być też przydatna dla deweloperów wewnętrznych modułów bibliotecznych w dużej, modułowej aplikacji.
Jeśli jesteś deweloperem aplikacji i chcesz dowiedzieć się więcej o optymalizacji aplikacji na Androida, przeczytaj artykuł Włączanie optymalizacji aplikacji. Aby dowiedzieć się, które biblioteki są odpowiednie, przeczytaj artykuł Mądrze wybieraj biblioteki.
Używaj generowania kodu zamiast refleksji
Jeśli to możliwe, używaj generowania kodu (codegen) zamiast odbicia. Generowanie kodu i refleksja to popularne metody unikania kodu szablonowego podczas programowania, ale generowanie kodu jest bardziej kompatybilne z optymalizatorem aplikacji, takim jak R8:
- W przypadku generowania kodu jest on analizowany i modyfikowany podczas procesu kompilacji. Ponieważ po kompilacji nie ma większych modyfikacji, optymalizator wie, który kod jest ostatecznie potrzebny, a który można bezpiecznie usunąć.
- W przypadku odbicia kod jest analizowany i modyfikowany w czasie działania programu. Kod nie jest w pełni gotowy, dopóki nie zostanie wykonany, więc optymalizator nie wie, który kod można bezpiecznie usunąć. Prawdopodobnie usunie kod, który jest używany dynamicznie przez odbicie podczas działania programu, co powoduje awarie aplikacji u użytkowników.
Wiele nowoczesnych bibliotek korzysta z generowania kodu zamiast z odbicia. Zobacz KSP, czyli wspólny punkt wejścia używany przez Room, Dagger2 i wiele innych bibliotek.
Kiedy odbicie jest w porządku
Jeśli musisz użyć odbicia, możesz to zrobić tylko w przypadku tych elementów:
- Określone typy docelowe (konkretne implementacje interfejsu lub podklasy)
- Kod z określoną adnotacją środowiska wykonawczego
Takie użycie refleksji ogranicza koszt w czasie działania i umożliwia pisanie reguł przechowywania kierowanych na konsumentów.
Ta konkretna i ukierunkowana forma refleksji jest wzorcem, który można zaobserwować zarówno w ramach Androida (np. podczas tworzenia aktywności, widoków i elementów rysowalnych), jak i w bibliotekach AndroidX (np. podczas tworzenia instancji klas WorkManager
ListenableWorkers
lub RoomDatabases
). Z kolei otwarta refleksja Gson nie nadaje się do używania w aplikacjach na Androida.
Typy reguł przechowywania w bibliotekach
W bibliotekach możesz mieć 2 rodzaje reguł przechowywania:
- Reguły przechowywania danych konsumentów muszą określać reguły, które zachowują wszystko, co odzwierciedla biblioteka. Jeśli biblioteka używa odbicia lub JNI do wywoływania kodu albo kodu zdefiniowanego przez aplikację klienta, te reguły muszą opisywać, który kod należy zachować. Biblioteki powinny zawierać reguły zachowywania danych użytkowników, które mają taki sam format jak reguły zachowywania danych aplikacji. Te reguły są dołączane do artefaktów biblioteki (plików AAR lub JAR) i automatycznie wykorzystywane podczas optymalizacji aplikacji na Androida, gdy biblioteka jest używana. Te reguły są przechowywane w pliku określonym za pomocą właściwości
consumerProguardFiles
w plikubuild.gradle.kts
(lubbuild.gradle
). Więcej informacji znajdziesz w artykule Tworzenie reguł przechowywania dla użytkowników. - Reguły przechowywania kompilacji biblioteki są stosowane podczas kompilowania biblioteki. Są one potrzebne tylko wtedy, gdy zdecydujesz się częściowo zoptymalizować bibliotekę w czasie kompilacji.
Muszą oni zapobiegać usunięciu publicznego interfejsu API biblioteki, ponieważ w przeciwnym razie nie będzie on dostępny w dystrybucji biblioteki, co oznacza, że deweloperzy aplikacji nie będą mogli z niej korzystać. Te reguły są przechowywane w pliku określonym za pomocą właściwości
proguardFiles
w plikubuild.gradle.kts
(lubbuild.gradle
). Więcej informacji znajdziesz w artykule Optymalizowanie kompilacji biblioteki AAR.
Tworzenie reguł przechowywania konsumenta
Oprócz ogólnych wskazówek dotyczących reguł przechowywania poniżej znajdziesz zalecenia skierowane do autorów bibliotek.
- Nie używaj nieodpowiednich reguł globalnych – unikaj umieszczania w pliku reguł keep biblioteki ustawień globalnych, takich jak
-dontobfuscate
lub-allowaccessmodification
, ponieważ mają one wpływ na wszystkie aplikacje, które korzystają z Twojej biblioteki. - Nie używaj znaku
-repackageclasses
w pliku reguł przechowywania dla konsumentów w bibliotece. Aby jednak zoptymalizować kompilację biblioteki, możesz użyć-repackageclasses
z wewnętrzną nazwą pakietu, np.<your.library.package>.internal
, w pliku reguł zachowywania kompilacji biblioteki. Może to zwiększyć wydajność biblioteki, nawet jeśli aplikacje, które z niej korzystają, nie są zoptymalizowane, ale zwykle nie jest to konieczne, ponieważ aplikacje również powinny być zoptymalizowane. Więcej informacji o optymalizacji bibliotek znajdziesz w artykule Optymalizacja dla autorów bibliotek. - W plikach reguł zachowywania biblioteki zadeklaruj wszystkie atrybuty, których potrzebujesz do jej działania, nawet jeśli mogą one pokrywać się z atrybutami zdefiniowanymi w
proguard-android-optimize.txt
. - Jeśli w dystrybucji biblioteki wymagane są te atrybuty, zachowaj je w pliku reguł zachowywania kompilacji biblioteki, a nie w pliku reguł zachowywania konsumenta biblioteki:
AnnotationDefault
EnclosingMethod
Exceptions
InnerClasses
RuntimeInvisibleAnnotations
RuntimeInvisibleParameterAnnotations
RuntimeInvisibleTypeAnnotations
RuntimeVisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeVisibleTypeAnnotations
Signature
- Autorzy bibliotek powinni zachować atrybut
RuntimeVisibleAnnotations
w regułach zachowywania konsumentów, jeśli adnotacje są używane w czasie działania programu. - Autorzy bibliotek nie powinni używać w regułach zachowywania konsumentów tych opcji globalnych:
-include
-basedirectory
-injars
-outjars
-libraryjars
-repackageclasses
-flattenpackagehierarchy
-allowaccessmodification
-overloadaggressively
-renamesourcefileattribute
-ignorewarnings
-addconfigurationdebugging
-printconfiguration
-printmapping
-printusage
-printseeds
-applymapping
-obfuscationdictionary
-classobfuscationdictionary
-packageobfuscationdictionary
Biblioteki AAR
Aby dodać reguły konsumenta dla biblioteki AAR, użyj opcji consumerProguardFiles
w skrypcie kompilacji modułu biblioteki na Androida. Więcej informacji znajdziesz w naszych wskazówkach dotyczących tworzenia modułów biblioteki.
Kotlin
android { defaultConfig { consumerProguardFiles("consumer-proguard-rules.pro") } ... }
Groovy
android { defaultConfig { consumerProguardFiles 'consumer-proguard-rules.pro' } ... }
Biblioteki JAR
Aby dołączyć reguły do biblioteki Kotlin/Java dostarczanej jako plik JAR, umieść plik reguł w katalogu META-INF/proguard/
w końcowym pliku JAR. Nazwa pliku może być dowolna.
Jeśli na przykład Twój kod znajduje się w <libraryroot>/src/main/kotlin
, umieść plik reguł dotyczących konsumentów w <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro
, a reguły zostaną dołączone we właściwym miejscu w wyjściowym pliku JAR.
Sprawdź, czy końcowe reguły pakietów JAR są prawidłowe, upewniając się, że znajdują się one w katalogu META-INF/proguard
.
Optymalizacja kompilacji biblioteki AAR (zaawansowana)
Zwykle nie trzeba bezpośrednio optymalizować kompilacji biblioteki, ponieważ możliwe optymalizacje w czasie kompilacji biblioteki są bardzo ograniczone. Dopiero podczas kompilacji aplikacji, gdy biblioteka jest do niej dołączana, R8 może dowiedzieć się, jak używane są wszystkie metody biblioteki i jakie parametry są przekazywane. Jako deweloper biblioteki musisz rozważyć wiele etapów optymalizacji i zachować zachowanie zarówno w czasie kompilacji biblioteki, jak i 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
bardzo 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 ma zostać zachowana podczas jej kompilacji. Jest to co najmniej publiczny interfejs API.consumerProguardFiles
są natomiast pakowane w bibliotece, aby wpływać na optymalizacje, które będą przeprowadzane później, podczas kompilowania aplikacji korzystającej z Twojej biblioteki.
Jeśli na przykład Twoja biblioteka używa odbicia do tworzenia klas wewnętrznych, może być konieczne zdefiniowanie reguł zachowywania zarówno w pliku proguardFiles
, jak i consumerProguardFiles
.
Jeśli w kompilacji biblioteki używasz -repackageclasses
, zmień pakiet klas na podpakiet wewnątrz pakietu biblioteki. Na przykład użyj -repackageclasses
'com.example.mylibrary.internal'
zamiast -repackageclasses 'internal'
.
Obsługa różnych wersji R8 (zaawansowana)
Możesz dostosować reguły tak, aby kierować reklamy na konkretne wersje R8. Dzięki temu biblioteka będzie optymalnie działać w projektach, które korzystają z nowszych wersji R8, a dotychczasowe reguły będą nadal używane w projektach ze starszymi wersjami R8.
Aby określić docelowe reguły R8, musisz umieścić je w katalogu
META-INF/com.android.tools
wewnątrz classes.jar
w pliku AAR lub w katalogu
META-INF/com.android.tools
w 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ć kilka podkatalogów o nazwach w formacie r8-from-<X>-upto-<Y>
, które wskazują, dla których wersji R8 są przeznaczone reguły. Każdy podkatalog może zawierać co najmniej 1 plik z regułami R8. Nazwy i rozszerzenia plików mogą być dowolne.
Pamiętaj, że części -from-<X>
i -upto-<Y>
są opcjonalne, wersja <Y>
jest wyłączna, a zakresy wersji są zwykle ciągłe, ale mogą się też nakładać.
Na przykład r8
, r8-upto-8.0.0
, r8-from-8.0.0-upto-8.2.0
i r8-from-8.2.0
to nazwy katalogów reprezentujące zestaw reguł R8, do których kierowane są reklamy. Z zasad w katalogu r8
można korzystać w dowolnych wersjach R8. Reguły w katalogu r8-from-8.0.0-upto-8.2.0
mogą być używane przez R8 w wersjach od 8.0.0 do 8.2.0 z wyłączeniem tej ostatniej.
Wtyczka Androida do obsługi Gradle używa tych informacji do wybierania wszystkich reguł, które mogą być używane przez bieżącą wersję R8. Jeśli biblioteka nie określa reguł R8, wtyczka Androida do Gradle wybierze reguły z lokalizacji starszego typu (proguard.txt
w przypadku pliku AAR lub META-INF/proguard/<ProGuard-rule-files>
w przypadku pliku JAR).