Выбирайте библиотеки с умом

Для оптимизации приложения необходимо использовать библиотеки, совместимые с оптимизацией Android. Если библиотека не настроена для оптимизации Android — например, если она использует рефлексию без включения соответствующих правил сохранения — она может не подойти для приложения Android. На этой странице объясняется, почему некоторые библиотеки лучше подходят для оптимизации приложений, и приводятся общие советы, которые помогут вам сделать выбор.

Общие советы по выбору библиотек

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

Предпочитать генерацию кода рефлексии.

Выбирайте библиотеки, использующие генерацию кода ( codegen ) вместо рефлексии. Благодаря codegen оптимизатор может легче определить, какой код фактически используется во время выполнения, а какой можно удалить. Определить, использует ли библиотека codegen или рефлексию, может быть сложно, но есть некоторые признаки — см. советы для получения дополнительной информации.

Для получения дополнительной информации о генерации кода и рефлексии см. раздел «Оптимизация для авторов библиотек» .

Проверьте наличие функции отражения (расширенные настройки).

Определить, использует ли библиотека рефлексию, можно, изучив её код. Если библиотека использует рефлексию, убедитесь, что она предоставляет соответствующие правила сохранения данных. Вероятно, библиотека использует рефлексию, если она выполняет следующие действия:

  • Использует классы или методы из пакетов kotlin.reflect или java.lang.reflect .
  • Использует функции Class.forName или classLoader.getClass .
  • Считывает аннотации во время выполнения, например, если сохраняет значение аннотации с помощью val value = myClass.getAnnotation() или val value = myMethod.getAnnotation() , а затем выполняет какие-либо действия с value .
  • Вызов методов осуществляется с использованием имени метода в виде строки, как в следующем примере:

    // Calls the private `processData` API with reflection
    myObject.javaClass.getMethod("processData", DataType::class.java)
    ?.invoke(myObject, data)
    

Проверьте наличие проблем с оптимизацией.

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

  • Библиотеки AndroidX и такие библиотеки, как Hilt, хорошо подходят для оптимизации приложений, поскольку в основном используют генерацию кода вместо рефлексии. Когда же они используют рефлексию, то предоставляют минимальные правила сохранения, позволяющие сохранять только необходимый код.
  • Библиотеки сериализации часто используют рефлексию, чтобы избежать шаблонного кода при создании экземпляров или сериализации объектов. Вместо подходов, основанных на рефлексии (таких как Gson для JSON), ищите библиотеки, которые используют генерацию кода, чтобы избежать этих проблем, например, используя Kotlin Serialization или Moshi с генерацией кода .
  • По возможности избегайте библиотек, содержащих правила сохранения данных на уровне всего пакета. Правила сохранения данных на уровне всего пакета могут помочь в устранении ошибок, но общие правила сохранения данных со временем следует уточнить, чтобы сохранять только необходимый код. Для получения дополнительной информации см. раздел «Постепенное внедрение оптимизаций» .
  • Библиотеки не должны требовать от вас копирования и вставки правил сохранения из документации в файл вашего проекта, особенно это касается правил сохранения, действующих на уровне всего пакета. В долгосрочной перспективе такие правила становятся обузой для разработчика приложения, и их сложно оптимизировать и изменять со временем.

Включите оптимизацию после добавления новой библиотеки.

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

Отфильтровать некорректные правила сохранения (расширенные настройки)

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

Следует избегать библиотек с правилами сохранения, которые удерживают код, который на самом деле следует удалить. Но если вам необходимо их использовать, вы можете отфильтровать эти правила, как показано в следующем коде:

// If you're using AGP 8.4 and higher
buildTypes {
    release {
        optimization.keepRules {
          it.ignoreFrom("com.somelibrary:somelibrary")
        }
    }
}

// If you're using AGP 7.3-8.3
buildTypes {
    release {
        optimization.keepRules {
          it.ignoreExternalDependencies("com.somelibrary:somelibrary")
        }
    }
}

Пример из практики: Почему Gson дает сбои при оптимизации.

Gson — это библиотека сериализации, которая часто вызывает проблемы с оптимизацией приложений, поскольку активно использует рефлексию. Следующий фрагмент кода показывает типичное использование Gson, что может легко привести к сбоям во время выполнения. Обратите внимание, что при использовании Gson для получения списка объектов User вы не вызываете конструктор и не передаете фабрику в функцию fromJson() . Создание или использование классов, определенных в приложении, без одного из следующих условий является признаком того, что библиотека может использовать открытую рефлексию:

  • Класс приложения, реализующий библиотеку, стандартный интерфейс или класс.
  • Плагин генерации кода, например KSP
class User(val name: String)
class UserList(val users: List<User>)

// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()

Чтобы понять, как R8 работает с Gson, ознакомьтесь с правилами обработки данных Gson . Когда R8 анализирует этот код и не обнаруживает нигде созданных экземпляров UserList или User , он может переименовывать поля или удалять конструкторы, которые, по-видимому, не используются, что приводит к сбою вашего приложения. Если вы используете какие-либо другие библиотеки аналогичным образом, вам следует убедиться, что они не будут мешать оптимизации приложения, и если это так, избегайте их использования.

Чтобы определить классы способом, совместимым с правилами обработки данных Gson, используйте следующий фрагмент кода в качестве примера:

class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)

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