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

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

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

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

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

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

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

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

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

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

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

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

Правила являются аддитивными

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

Проверка использования отражения (расширенная)

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

  • Использует классы или методы из пакетов 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)
    

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

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

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

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