Как автор библиотеки, вы должны обеспечить разработчикам приложений возможность легко интегрировать вашу библиотеку в свои приложения, сохраняя при этом высокое качество пользовательского опыта. Это означает, что ваша библиотека должна быть совместима с оптимизацией для Android (R8) без необходимости дополнительной настройки со стороны разработчика — или же необходимо задокументировать, что библиотека может быть непригодна для использования на Android. Крайне важно, чтобы библиотеки, предназначенные для использования на Android, не препятствовали важным оптимизациям приложений и соответствовали дополнительным требованиям к оптимизации .
Данная документация предназначена для разработчиков опубликованных библиотек, но может быть полезна и для разработчиков внутренних библиотечных модулей в больших модульных приложениях.
Если вы разработчик приложений и хотите узнать об оптимизации вашего Android-приложения, см. раздел «Включение оптимизации приложения» . Чтобы узнать, какие библиотеки подходят для использования, см. раздел «Разумный выбор библиотек» .
Разберитесь в типах правил сохранения.
В библиотеках могут быть два различных типа правил сохранения:
- Правила сохранения данных потребителем должны определять правила, которые сохраняют все, что библиотека использует для рефлексии. Если библиотека использует рефлексию или JNI для вызова своего кода или кода, определенного клиентским приложением, эти правила должны описывать, какой код необходимо сохранить. Библиотеки должны упаковывать правила сохранения данных потребителем, используя тот же формат, что и правила сохранения данных приложением. Эти правила упаковываются в артефакты библиотеки (AAR или JAR) и автоматически используются во время оптимизации Android-приложения при использовании библиотеки. Эти правила хранятся в файле, указанном с помощью свойства
consumerProguardFilesв файлеbuild.gradle.kts(илиbuild.gradle). Для получения дополнительной информации см. раздел «Написание правил сохранения данных потребителем» . - Правила сохранения библиотеки применяются при сборке вашей библиотеки. Они необходимы только в том случае, если вы решили частично оптимизировать свою библиотеку во время сборки. Они должны предотвратить удаление публичного API библиотеки, иначе публичный API не будет присутствовать в дистрибутиве библиотеки, а это значит, что разработчики приложений не смогут использовать библиотеку. Эти правила хранятся в файле, указанном с помощью свойства
proguardFilesв вашем файлеbuild.gradle.kts(илиbuild.gradle). Для получения дополнительной информации см. раздел «Оптимизация сборки AAR-библиотеки» .
Требования и рекомендации по оптимизации
Конфигурация R8 в библиотеках оказывает глобальное влияние на конечный размер исполняемого файла и производительность используемого приложения. Помимо общих рекомендаций по сохранению файлов , авторы библиотек должны придерживаться определенных требований и учитывать дополнительные рекомендации.
Соблюдайте требования по оптимизации.
Неэффективность в библиотеках является одной из основных причин раздувания приложений, нерационального использования памяти, медленного запуска и ошибок ANR (Application Not Responding). Библиотеки должны избегать нарушений следующих требований, чтобы не допустить существенного снижения качества приложений и удобства использования.
Никаких общих или общепакетных правил хранения: Ваша библиотека не должна содержать общих правил хранения, которые сохраняют большую часть кода в вашей библиотеке или в другой библиотеке. Общие правила хранения могут решить проблемы со сбоями в краткосрочной перспективе, но они увеличивают размер всех приложений, использующих вашу библиотеку.
Не включайте правила сохранения для всего пакета (например,
-keep class com.mylibrary.** {*; }) для пакетов в вашей библиотеке или других библиотек, на которые вы ссылаетесь. Такие правила ограничивают оптимизацию этих пакетов во всех приложениях, которые используют вашу библиотеку.Никаких неподходящих глобальных правил: никогда не используйте глобальные параметры, такие как
-dontobfuscateили-allowaccessmodification.По возможности используйте генерацию кода вместо рефлексии: по возможности используйте генерацию кода ( codegen ) вместо рефлексии. И генерация кода, и рефлексия — распространенные подходы для избежания шаблонного кода в программировании, но генерация кода лучше совместима с оптимизатором приложений, таким как R8.
При использовании генерации кода код анализируется и модифицируется в процессе сборки. Поскольку после компиляции существенных изменений не происходит, оптимизатор знает, какой код в конечном итоге необходим, а какой можно безопасно удалить.
При использовании рефлексии код анализируется и изменяется во время выполнения. Поскольку код фактически не завершается до момента его выполнения, оптимизатор не знает, какой код можно безопасно удалить. Скорее всего, он удалит код, используемый динамически с помощью рефлексии во время выполнения, что приводит к сбоям приложения для пользователей.
Многие современные библиотеки используют генерацию кода вместо рефлексии. В качестве распространенной точки входа можно использовать KSP , которую применяют Room , Dagger2 и многие другие.
Поддержка полного режима R8: Ваша библиотека не должна аварийно завершать работу при включении полного режима R8 . Полный режим R8 — это рекомендуемый режим для использования R8, и он является режимом по умолчанию, начиная с AGP 8.0, который был стабилен в 2023 году. Если ваша библиотека аварийно завершает работу в R8, решение состоит в том, чтобы определить конкретную точку входа отражения или JNI и добавить целевое правило, а не сохранять весь пакет целиком.
Дополнительные рекомендации
Помимо требований к оптимизации, ниже приведены дополнительные рекомендации.
- Не используйте
-repackageclassesв файле правил сохранения данных для вашей библиотеки. Однако для оптимизации сборки библиотеки вы можете использовать-repackageclassesс внутренним именем пакета, например,<your.library.package>.internal, в файле правил сохранения данных для сборки вашей библиотеки. Это может повысить эффективность вашей библиотеки в неоптимизированных приложениях. Однако, как правило, это не обязательно, поскольку приложения также должны быть оптимизированы. - В файлах правил сохранения вашей библиотеки укажите все необходимые атрибуты для ее функционирования, даже если они могут частично совпадать с атрибутами, определенными в
proguard-android-optimize.txt. - Если вам необходимы следующие атрибуты в дистрибутиве вашей библиотеки, укажите их в файле правил сохранения при сборке библиотеки, а не в файле правил сохранения при потреблении библиотеки:
-
AnnotationDefault -
EnclosingMethod -
Exceptions -
InnerClasses -
RuntimeInvisibleAnnotations -
RuntimeInvisibleParameterAnnotations -
RuntimeInvisibleTypeAnnotations -
RuntimeVisibleAnnotations -
RuntimeVisibleParameterAnnotations -
RuntimeVisibleTypeAnnotations -
Signature
-
- Если аннотации используются во время выполнения, авторам библиотек следует сохранять атрибут
RuntimeVisibleAnnotationsв правилах хранения данных потребителя. - Авторам библиотек не следует использовать следующие глобальные параметры в правилах хранения данных потребителями:
-
-include -
-basedirectory -
-injars -
-outjars -
-libraryjars -
-repackageclasses -
-flattenpackagehierarchy -
-allowaccessmodification -
-renamesourcefileattribute -
-ignorewarnings -
-addconfigurationdebugging -
-printconfiguration -
-printmapping -
-printusage -
-printseeds -
-applymapping -
-obfuscationdictionary -
-classobfuscationdictionary -
-packageobfuscationdictionary
-
Когда размышление допустимо
Если вам необходимо использовать отражение, то отражение следует производить только в одном из следующих случаев:
- Конкретные целевые типы (конкретные реализации интерфейсов или подклассы)
- Код с использованием специальной аннотации времени выполнения
Использование рефлексии таким образом ограничивает затраты времени выполнения и позволяет писать целевые правила хранения для потребителей .
Эта специфическая и целенаправленная форма рефлексии — шаблон, который можно наблюдать как в фреймворке Android (например, при создании активностей, представлений и изображений), так и в библиотеках AndroidX (например, при создании WorkManager ListenableWorkers или RoomDatabases ). В отличие от этого, открытая форма рефлексии Gson не подходит для использования в приложениях Android .
Распространенные заблуждения
Несколько распространенных заблуждений могут привести к неправильной настройке R8. К ним относятся следующие:
Неправильное понимание оптимизаций R8 : Вопреки распространенному мнению, оптимизации R8 не ограничиваются только обфускацией, но также включают сокращение кода и логическую оптимизацию с помощью встраивания методов и методов слияния классов. Для получения дополнительной информации см. Обзор оптимизаций R8 .
Обход оптимизации обфусцированных библиотек : распространённая ошибка — исключение библиотеки из оптимизации, поскольку она была оптимизирована или обфусцирована при компиляции в AAR (Android Archive) или JAR (Java Archive). Оптимизация во время сборки библиотеки ограничена, и ваше приложение не должно отключать оптимизацию библиотеки, включая её в правило сохранения. Для получения дополнительной информации см. раздел «Оптимизация сборки AAR-библиотеки» .
Неправильное понимание параметра
-keepПравило-keepпредотвращает выполнение R8 каких-либо этапов оптимизации . Для получения дополнительной информации см. раздел «Выбор правильного параметра keep» .
Настройка упаковки правил
Для обеспечения правильного применения правил хранения товаров потребителями необходимо соответствующим образом упаковывать их в зависимости от формата вашей библиотеки.
библиотеки AAR
Чтобы добавить правила обработки данных для библиотеки AAR, используйте параметр consumerProguardFiles в скрипте сборки модуля библиотеки Android. Для получения дополнительной информации см. наши рекомендации по созданию модулей библиотек .
Котлин
android {
defaultConfig {
consumerProguardFiles("consumer-proguard-rules.pro")
}
...
}
Классный
android {
defaultConfig {
consumerProguardFiles 'consumer-proguard-rules.pro'
}
...
}
JAR-библиотеки
Чтобы включить правила в вашу библиотеку Kotlin или Java, поставляемую в виде JAR-файла, поместите файл правил в каталог META-INF/proguard/ итогового JAR-файла, указав любое имя файла. Например, если ваш код находится в <libraryroot>/src/main/kotlin , поместите файл правил потребителя в <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro , и правила будут включены в нужное место в вашем выходном JAR-файле.
Убедитесь, что окончательный JAR-файл корректно содержит правила, проверив, находятся ли эти правила в каталоге META-INF/proguard .
Оптимизация сборки библиотеки AAR (расширенные настройки)
Как правило, вам не нужно оптимизировать сборку библиотеки напрямую, поскольку возможности оптимизации на этапе сборки библиотеки очень ограничены. Разработчику библиотеки необходимо учитывать множество этапов оптимизации и сохранять поведение как на этапе сборки библиотеки, так и на этапе сборки приложения, прежде чем приступать к оптимизации самой библиотеки.
Если вы все же хотите оптимизировать свою библиотеку во время сборки, это поддерживается плагином Android Gradle.
Котлин
android {
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
configureEach {
consumerProguardFiles("consumer-rules.pro")
}
}
}
Классный
android {
buildTypes {
release {
minifyEnabled true
proguardFiles
getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
configureEach {
consumerProguardFiles "consumer-rules.pro"
}
}
}
Обратите внимание, что поведение proguardFiles существенно отличается от поведения consumerProguardFiles :
-
proguardFilesиспользуются во время сборки, часто вместе сgetDefaultProguardFile("proguard-android-optimize.txt"), чтобы определить, какая часть вашей библиотеки должна быть сохранена во время сборки. Как минимум, это ваш публичный API. -
consumerProguardFilesнапротив, упаковываются в библиотеку, чтобы влиять на то, какие оптимизации будут выполняться позже, во время сборки приложения, использующего вашу библиотеку.
Например, если ваша библиотека использует рефлексию для создания внутренних классов, вам может потребоваться определить правила сохранения данных как в proguardFiles , так и consumerProguardFiles .
Если вы используете -repackageclasses при сборке вашей библиотеки, перепакуйте классы в подпакет внутри пакета вашей библиотеки. Например, используйте -repackageclasses 'com.example.mylibrary.internal' вместо -repackageclasses 'internal' .
Поддержка различных версий R8 (расширенные возможности)
Вы можете настраивать правила для конкретных версий R8. Это позволяет вашей библиотеке оптимально работать в проектах, использующих более новые версии R8, и в то же время позволяет продолжать использовать существующие правила в проектах со старыми версиями R8.
Чтобы указать целевые правила R8, необходимо включить их в каталог META-INF/com.android.tools внутри classes.jar в AAR-архиве или в каталог META-INF/com.android.tools в 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)
В каталоге META-INF/com.android.tools может находиться несколько подкаталогов с именами в формате r8-from-<X>-upto-<Y> , указывающими, для каких версий R8 написаны правила. В каждом подкаталоге может находиться один или несколько файлов, содержащих правила R8, с любыми именами файлов и расширениями.
Обратите внимание, что части -from-<X> и -upto-<Y> являются необязательными, версия <Y> является исключительной , а диапазоны версий обычно непрерывны, но могут также перекрываться.
Например, r8 , r8-upto-8.0.0 , r8-from-8.0.0-upto-8.2.0 и r8-from-8.2.0 — это имена каталогов, представляющие набор целевых правил R8. Правила в каталоге r8 могут использоваться любыми версиями R8. Правила в каталоге r8-from-8.0.0-upto-8.2.0 могут использоваться R8 начиная с версии 8.0.0 и заканчивая версией 8.2.0 включительно.
Плагин Android Gradle использует эту информацию для выбора всех правил, которые могут быть использованы текущей версией R8. Если библиотека не указывает целевые правила для R8, плагин Android Gradle выберет правила из устаревших расположений ( proguard.txt для AAR-файла или META-INF/proguard/<ProGuard-rule-files> для JAR-файла).