Оптимизация для авторов библиотеки

Как автор библиотеки, вы должны убедиться, что разработчики приложений могут легко интегрировать вашу библиотеку в свои приложения, сохраняя при этом высокое качество пользовательского опыта. Вы должны убедиться, что ваша библиотека совместима с оптимизацией для Android без дополнительной настройки — или задокументировать, что библиотека может быть непригодна для использования на Android.

Данная документация предназначена для разработчиков опубликованных библиотек, но может быть полезна и для разработчиков внутренних библиотечных модулей в больших модульных приложениях.

Если вы разработчик приложений и хотите узнать об оптимизации вашего Android-приложения, см. раздел «Включение оптимизации приложения» . Чтобы узнать, какие библиотеки подходят для использования, см. раздел «Разумный выбор библиотек» .

Используйте генерацию кода вместо рефлексии.

По возможности используйте генерацию кода ( codegen ) вместо рефлексии. И генерация кода, и рефлексия — распространенные подходы для избежания шаблонного кода в программировании, но генерация кода лучше совместима с оптимизатором приложений, таким как R8:

  • При использовании генерации кода код анализируется и модифицируется в процессе сборки. Поскольку после компиляции существенных изменений не происходит, оптимизатор знает, какой код в конечном итоге необходим, а какой можно безопасно удалить.
  • При использовании рефлексии код анализируется и изменяется во время выполнения. Поскольку код фактически не завершается до момента его выполнения, оптимизатор не знает, какой код можно безопасно удалить. Скорее всего, он удалит код, используемый динамически с помощью рефлексии во время выполнения, что приводит к сбоям приложения для пользователей.

Многие современные библиотеки используют генерацию кода вместо рефлексии. В качестве распространенной точки входа можно использовать KSP , которую применяют Room , Dagger2 и многие другие.

Когда размышление допустимо

Если вам необходимо использовать отражение, то отражение следует производить только в одном из следующих случаев:

  • Конкретные целевые типы (конкретные реализации интерфейсов или подклассы)
  • Код с использованием специальной аннотации времени выполнения

Использование рефлексии таким образом ограничивает затраты времени выполнения и позволяет писать целевые правила хранения для потребителей .

Эта специфическая и целенаправленная форма рефлексии — шаблон, который можно наблюдать как в фреймворке Android (например, при создании активностей, представлений и изображений), так и в библиотеках AndroidX (например, при создании WorkManager ListenableWorkers или RoomDatabases ). В отличие от этого, открытая форма рефлексии Gson не подходит для использования в приложениях Android .

Типы правил хранения в библиотеках

В библиотеках могут быть два различных типа правил сохранения:

  • Правила сохранения данных потребителем должны определять правила, которые сохраняют все, что библиотека использует для рефлексии. Если библиотека использует рефлексию или JNI для вызова своего кода или кода, определенного клиентским приложением, эти правила должны описывать, какой код необходимо сохранить. Библиотеки должны упаковывать правила сохранения данных потребителем, используя тот же формат, что и правила сохранения данных приложением. Эти правила упаковываются в артефакты библиотеки (AAR или JAR) и автоматически используются во время оптимизации Android-приложения при использовании библиотеки. Эти правила хранятся в файле, указанном с помощью свойства consumerProguardFiles в файле build.gradle.kts (или build.gradle ). Для получения дополнительной информации см. раздел «Написание правил сохранения данных потребителем» .
  • Правила сохранения библиотеки применяются при сборке вашей библиотеки. Они необходимы только в том случае, если вы решили частично оптимизировать свою библиотеку во время сборки. Они должны предотвратить удаление публичного API библиотеки, иначе публичный API не будет присутствовать в дистрибутиве библиотеки, а это значит, что разработчики приложений не смогут использовать библиотеку. Эти правила хранятся в файле, указанном с помощью свойства proguardFiles в вашем файле build.gradle.kts (или build.gradle ). Для получения дополнительной информации см. раздел «Оптимизация сборки AAR-библиотеки» .

Напишите правила сохранения данных потребителем.

Помимо общих рекомендаций по соблюдению правил хранения , ниже приведены советы, специально предназначенные для авторов библиотечных материалов.

  • Не используйте неподходящие глобальные правила — избегайте размещения глобальных настроек, таких как -dontobfuscate или -allowaccessmodification в файле правил хранения данных для пользователей вашей библиотеки, поскольку они влияют на все приложения, использующие вашу библиотеку.
  • Не следует включать правила сохранения, действующие на уровне всего пакета, например, -keep class com.mylibrary.** { *; } . Такие правила ограничивают оптимизацию всей библиотеки, влияя на размер всех приложений, использующих эту библиотеку.
  • Не используйте -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
    • -overloadaggressively
    • -renamesourcefileattribute
    • -ignorewarnings
    • -addconfigurationdebugging
    • -printconfiguration
    • -printmapping
    • -printusage
    • -printseeds
    • -applymapping
    • -obfuscationdictionary
    • -classobfuscationdictionary
    • -packageobfuscationdictionary

библиотеки 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 (расширенные настройки)

Как правило, вам не нужно оптимизировать сборку библиотеки напрямую, поскольку возможности оптимизации на этапе сборки библиотеки очень ограничены. Только во время сборки приложения, когда библиотека включается в состав приложения, R8 может узнать, как используются все методы библиотеки и какие параметры передаются. Как разработчику библиотеки, вам необходимо учитывать множество этапов оптимизации и сохранять поведение как на этапе сборки библиотеки, так и на этапе сборки приложения, прежде чем оптимизировать саму библиотеку.

Если вы все же хотите оптимизировать свою библиотеку во время сборки, это поддерживается плагином 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-файла).