Если minSdk
вашего приложения равен API 20 или ниже , а само приложение и библиотеки, на которые оно ссылается, превышают 65 536 методов, вы столкнетесь со следующей ошибкой сборки, которая указывает на то, что ваше приложение достигло предела архитектуры сборки Android:
trouble writing output: Too many field references: 131000; max is 65536. You may try using --multi-dex option.
Более старые версии системы сборки сообщают о другой ошибке, которая указывает на ту же проблему:
Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536
Эти ошибки отображают общее число: 65536. Это число представляет собой общее количество ссылок, которые может вызвать код в одном исполняемом файле байт-кода Dalvik (DEX). На этой странице объясняется, как обойти это ограничение, включив конфигурацию приложения, известную как multidex , которая позволяет вашему приложению создавать и читать несколько файлов DEX.
О пределе ссылок в 64 КБ
Файлы приложений Android (APK) содержат исполняемые файлы байт-кода в виде исполняемых файлов Dalvik (DEX) , которые содержат скомпилированный код, используемый для запуска вашего приложения. Спецификация Dalvik Executable ограничивает общее количество методов, на которые можно ссылаться в одном DEX-файле, 65 536, включая методы фреймворка Android, методы библиотек и методы в вашем собственном коде.
В контексте компьютерных наук термин «кило», или К , обозначает 1024 (или 2^10). Поскольку 65 536 равно 64x1024, этот предел называется _пределом 64К_.Поддержка Multidex до Android 5.0
Версии платформы до Android 5.0 (API уровня 21) используют среду выполнения Dalvik для выполнения кода приложения. По умолчанию Dalvik ограничивает приложения одним файлом байт-кода classes.dex
на APK. Чтобы обойти это ограничение, добавьте библиотеку multidex в файл build.gradle
или build.gradle.kts
на уровне модуля:
Круто
dependencies { def multidex_version = "2.0.1" implementation "androidx.multidex:multidex:$multidex_version" }
Котлин
dependencies { val multidex_version = "2.0.1" implementation("androidx.multidex:multidex:$multidex_version") }
Эта библиотека становится частью основного DEX-файла вашего приложения, а затем управляет доступом к дополнительным DEX-файлам и содержащемуся в них коду. Чтобы просмотреть текущие версии этой библиотеки, см. раздел «Версии multidex» .
Более подробную информацию смотрите в разделе о том, как настроить приложение для multidex .Поддержка Multidex для Android 5.0 и выше
В Android 5.0 (уровень API 21) и выше используется среда выполнения ART, которая изначально поддерживает загрузку нескольких DEX-файлов из APK-файлов. ART выполняет предварительную компиляцию во время установки приложения, сканируя файлы classes N .dex
и компилируя их в один OAT-файл для выполнения на устройстве Android. Таким образом, если minSdkVersion
равен 21 или выше, multidex включен по умолчанию, и библиотека multidex вам не нужна.
Дополнительную информацию о среде выполнения Android 5.0 можно найти в статьях Android Runtime (ART) и Dalvik .
Примечание: При запуске приложения в Android Studio сборка оптимизируется для целевых устройств, на которых оно развёртывается. Это включает в себя включение multidex, если целевые устройства работают под управлением Android 5.0 и выше. Поскольку эта оптимизация применяется только при развёртывании приложения в Android Studio, вам всё равно может потребоваться настроить сборку релиза для multidex, чтобы избежать ограничения в 64 КБ.
Избегайте ограничения в 64 КБ
Прежде чем настраивать приложение для использования ссылок на методы размером 64 КБ или более, примите меры по сокращению общего количества ссылок, вызываемых кодом вашего приложения, включая методы, определенные кодом вашего приложения или включенными библиотеками.
Следующие стратегии помогут вам избежать достижения контрольного лимита DEX:
- Проверьте прямые и транзитивные зависимости вашего приложения.
- Подумайте, перевешивает ли ценность любой крупной зависимости от библиотеки, включаемой в приложение, объём добавляемого в него кода. Распространенная, но проблемная практика — включать очень большую библиотеку, потому что несколько вспомогательных методов оказались полезными. Сокращение зависимостей кода приложения часто помогает избежать ограничения на количество ссылок DEX.
- Удалить неиспользуемый код с помощью R8
- Включите сжатие кода для запуска R8 в ваших релизных сборках. Включите сжатие кода, чтобы гарантировать отсутствие неиспользуемого кода в APK-файлах. Если сжатие кода настроено правильно, оно также может удалить неиспользуемый код и ресурсы из ваших зависимостей.
Использование этих методов может помочь вам уменьшить общий размер вашего APK и избежать необходимости использования multidex в вашем приложении.
Настройте свое приложение для мультидекса
Примечание: еслиminSdkVersion
установлен на 21 или выше, multidex включен по умолчанию и вам не нужна библиотека multidex. Если minSdkVersion
равно 20 или ниже, то необходимо использовать библиотеку multidex и внести следующие изменения в проект приложения:
Измените файл
build.gradle
на уровне модуля, чтобы включить multidex, и добавьте библиотеку multidex в качестве зависимости, как показано здесь:Круто
android { defaultConfig { ... minSdkVersion 15 targetSdkVersion 33 multiDexEnabled true } ... } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Котлин
android { defaultConfig { ... minSdk = 15 targetSdk = 33 multiDexEnabled = true } ... } dependencies { implementation("androidx.multidex:multidex:2.0.1") }
- В зависимости от того, переопределяете ли вы класс
Application
, выполните одно из следующих действий:Если вы не переопределяете класс
Application
, отредактируйте файл манифеста, чтобы установитьandroid:name
в теге<application>
следующим образом:<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <application android:name="androidx.multidex.MultiDexApplication" > ... </application> </manifest>
Если вы переопределяете класс
Application
, измените его так, чтобы он расширялMultiDexApplication
, как показано ниже:Котлин
class MyApplication : MultiDexApplication() {...}
Ява
public class MyApplication extends MultiDexApplication { ... }
Если вы переопределяете класс
Application
, но при этом невозможно изменить базовый класс, то вместо этого переопределите методattachBaseContext()
и вызовитеMultiDex.install(this)
, чтобы включить multidex:Котлин
class MyApplication : SomeOtherApplication() { override fun attachBaseContext(base: Context) { super.attachBaseContext(base) MultiDex.install(this) } }
Ява
public class MyApplication extends SomeOtherApplication { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } }
Внимание: Не выполняйте
MultiDex.install()
или любой другой код через рефлексию или JNI до завершенияMultiDex.install()
. Трассировка Multidex не будет отслеживать эти вызовы, что приведёт к исключениюClassNotFoundException
или ошибкам проверки из-за неправильного разделения классов между DEX-файлами.
Теперь при сборке приложения инструменты сборки Android создают основной DEX-файл ( classes.dex
) и вспомогательные DEX-файлы ( classes2.dex
, classes3.dex
и т. д.) по мере необходимости. Затем система сборки упаковывает все DEX-файлы в ваш APK.
Во время выполнения, вместо поиска только в основном файле classes.dex
, API multidex используют специальный загрузчик классов для поиска ваших методов во всех доступных файлах DEX.
Ограничения библиотеки multidex
Библиотека multidex имеет ряд известных ограничений. При включении библиотеки в конфигурацию сборки приложения учтите следующее:
- Установка DEX-файлов во время запуска системы в раздел данных устройства сложна и может привести к ошибкам «Приложение не отвечает» (ANR), если вторичные DEX-файлы имеют большой размер. Чтобы избежать этой проблемы, включите сокращение кода , чтобы минимизировать размер DEX-файлов и удалить неиспользуемые фрагменты кода.
- В версиях Android до 5.0 (уровень API 21) использование multidex недостаточно для обхода ограничения linearalloc ( проблема 37008143 ). Это ограничение было увеличено в Android 4.0 (уровень API 14), но это не решило проблему полностью.
В версиях Android ниже 4.0 лимит линейного распределения памяти может быть достигнут раньше, чем лимит индекса DEX. Поэтому, если вы ориентируетесь на уровни API ниже 14, тщательно протестируйте их на этих версиях платформы, поскольку ваше приложение может столкнуться с проблемами при запуске или загрузке определённых групп классов.
Сокращение кода может уменьшить или, возможно, устранить эти проблемы.
Объявите требуемые классы в основном файле DEX
При сборке каждого DEX-файла для приложения Multidex инструменты сборки выполняют сложный процесс принятия решений, чтобы определить, какие классы необходимы в основном DEX-файле для успешного запуска приложения. Если какой-либо класс, необходимый при запуске, отсутствует в основном DEX-файле, приложение аварийно завершает работу с ошибкой java.lang.NoClassDefFoundError
.
Инструменты сборки распознают пути кода, к которому осуществляется прямой доступ из кода вашего приложения. Однако эта проблема может возникнуть, когда пути кода менее заметны, например, если используемая вами библиотека имеет сложные зависимости. Например, если код использует интроспекцию или вызов методов Java из машинного кода, то эти классы могут не распознаваться как требуемые в основном DEX-файле.
Если вы получили java.lang.NoClassDefFoundError
, необходимо вручную указать дополнительные классы, необходимые в основном DEX-файле, объявив их с помощью свойства multiDexKeepProguard
в типе сборки. Если класс найден в файле multiDexKeepProguard
, он добавляется в основной DEX-файл.
свойство multiDexKeepProguard
Файл multiDexKeepProguard
использует тот же формат, что и ProGuard, и поддерживает всю грамматику ProGuard. Подробнее о настройке сохраняемых данных в приложении см. в разделе «Настройка сохраняемого кода» .
Файл, указанный в multiDexKeepProguard
, должен содержать параметры -keep
в любом допустимом синтаксисе ProGuard. Например, -keep com.example.MyClass.class
. Вы можете создать файл multidex-config.pro
следующего вида:
-keep class com.example.MyClass -keep class com.example.MyClassToo
Если вы хотите указать все классы в пакете, файл выглядит так:
-keep class com.example.** { *; } // All classes in the com.example package
Затем вы можете объявить этот файл для типа сборки следующим образом:
Круто
android { buildTypes { release { multiDexKeepProguard file('multidex-config.pro') ... } } }
Котлин
android { buildTypes { getByName("release") { multiDexKeepProguard = file("multidex-config.pro") ... } } }
Оптимизация multidex в сборках разработки
Конфигурация multidex требует значительного увеличения времени сборки, поскольку система сборки должна принимать сложные решения о том, какие классы должны быть включены в первичный DEX-файл, а какие — во вторичные DEX-файлы. Это означает, что инкрементальные сборки с использованием multidex обычно занимают больше времени и могут потенциально замедлить процесс разработки.
Чтобы сократить время инкрементальной сборки, используйте предварительный дексинг для повторного использования выходных данных multidex между сборками. Предварительный дексинг основан на формате ART, доступном только в Android 5.0 (API уровня 21) и выше. Если вы используете Android Studio, IDE автоматически использует предварительный дексинг при развертывании приложения на устройстве под управлением Android 5.0 (API уровня 21) и выше. Однако, если вы запускаете сборки Gradle из командной строки, вам необходимо установить minSdkVersion
равным 21 или выше, чтобы включить предварительный дексинг.
minSdkVersion
, как показано: Круто
android { defaultConfig { ... multiDexEnabled true // The default minimum API level you want to support. minSdkVersion 15 } productFlavors { // Includes settings you want to keep only while developing your app. dev { // Enables pre-dexing for command-line builds. When using // Android Studio 2.3 or higher, the IDE enables pre-dexing // when deploying your app to a device running Android 5.0 // (API level 21) or higher, regardless of minSdkVersion. minSdkVersion 21 } prod { // If you've configured the defaultConfig block for the production version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to include this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Котлин
android { defaultConfig { ... multiDexEnabled = true // The default minimum API level you want to support. minSdk = 15 } productFlavors { // Includes settings you want to keep only while developing your app. create("dev") { // Enables pre-dexing for command-line builds. When using // Android Studio 2.3 or higher, the IDE enables pre-dexing // when deploying your app to a device running Android 5.0 // (API level 21) or higher, regardless of minSdkVersion. minSdk = 21 } create("prod") { // If you've configured the defaultConfig block for the production version of // your app, you can leave this block empty and Gradle uses configurations in // the defaultConfig block instead. You still need to include this flavor. // Otherwise, all variants use the "dev" flavor configurations. } } buildTypes { getByName("release") { isMinifyEnabled = true proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } } } dependencies { implementation("androidx.multidex:multidex:2.0.1") }
Чтобы узнать больше о стратегиях повышения скорости сборки из Android Studio или командной строки, прочтите статью «Оптимизация скорости сборки» . Подробнее об использовании вариантов сборки см. в статье «Настройка вариантов сборки» .
Совет: Если у вас разные варианты сборки для разных потребностей Multidex, вы можете предоставить отдельный файл манифеста для каждого варианта, чтобы имя тега <application>
изменялось только в файле для API уровня 20 и ниже. Вы также можете создать отдельный подкласс Application
для каждого варианта, чтобы только подкласс для API уровня 20 и ниже расширял класс MultiDexApplication
или вызывал MultiDex.install(this)
.
Тестовые мультидекс-приложения
При написании тестов инструментирования для приложений Multidex дополнительная настройка не требуется, если вы используете инструментирование MonitoringInstrumentation
или AndroidJUnitRunner
. Если вы используете другой Instrumentation
, необходимо переопределить его метод onCreate()
следующим кодом:
Котлин
fun onCreate(arguments: Bundle) { MultiDex.install(targetContext) super.onCreate(arguments) ... }
Ява
public void onCreate(Bundle arguments) { MultiDex.install(getTargetContext()); super.onCreate(arguments); ... }