Если для вашего приложения установлен 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, методы библиотек и методы в вашем собственном коде.
В контексте информатики термин кило, или K , обозначает 10²⁴ (или 2¹⁰). Поскольку 65 536 равно 64 × 10²⁴, этот предел называется эталонным пределом 64K.Поддержка 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 000 или более ссылок на методы, примите меры по сокращению общего количества ссылок, вызываемых кодом приложения, включая методы, определенные кодом приложения или включенными библиотеками.
Следующие стратегии помогут вам избежать достижения лимита DEX:
- Проверьте прямые и транзитивные зависимости вашего приложения.
- Подумайте, перевешивает ли ценность любой крупной библиотеки, которую вы включаете в свое приложение, объем добавляемого в него кода. Распространенная, но проблемная модель — включение очень большой библиотеки только потому, что несколько вспомогательных методов оказались полезными. Сокращение зависимостей кода вашего приложения часто помогает избежать ограничения на количество ссылок в DEX-редакторе.
- Удаление неиспользуемого кода с помощью R8
- Включите сжатие кода , чтобы запускать R8 для ваших релизных сборок. Включение сжатия кода поможет гарантировать, что вы не будете поставлять неиспользуемый код вместе с вашими APK-файлами. Если сжатие кода настроено правильно, оно также может удалять неиспользуемый код и ресурсы из ваших зависимостей.
Использование этих методов поможет вам уменьшить общий размер APK-файла и избежать необходимости использования multidex в вашем приложении.
Настройте ваше приложение для работы с Multidex.
Примечание: Если значениеminSdkVersion равно 21 или выше, multidex включен по умолчанию, и вам не потребуется библиотека multidex. Если значение minSdkVersion равно 20 или ниже, вам необходимо использовать библиотеку multidex и внести следующие изменения в проект вашего приложения:
Измените файл
build.gradleна уровне модуля, чтобы включить поддержку multidex и добавить библиотеку multidex в качестве зависимости, как показано здесь:Классный
android { defaultConfig { ... minSdkVersion 15 targetSdkVersion 36 multiDexEnabled true } ... } dependencies { implementation "androidx.multidex:multidex:2.0.1" }
Котлин
android { defaultConfig { ... minSdk = 15 targetSdk = 36 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() {...}
Java
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) } }
Java
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 вы можете достичь предела linearalloc раньше, чем предела индекса DEX. Поэтому, если вы ориентируетесь на уровни API ниже 14, тщательно протестируйте приложение на этих версиях платформы, поскольку у него могут возникнуть проблемы при запуске или при загрузке определенных групп классов.
Сокращение объема кода может уменьшить или даже полностью устранить эти проблемы.
Объявите классы, необходимые в основном DEX-файле.
При сборке каждого DEX-файла для мультидексного приложения инструменты сборки принимают сложные решения, чтобы определить, какие классы необходимы в основном 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 обычно занимают больше времени и потенциально могут замедлить процесс разработки.
Чтобы сократить время инкрементальной сборки, используйте pre-dexing для повторного использования выходных данных multidex между сборками. Pre-dexing использует формат ART, доступный только в Android 5.0 (уровень API 21) и выше. Если вы используете Android Studio, IDE автоматически использует pre-dexing при развертывании вашего приложения на устройстве под управлением Android 5.0 (уровень API 21) или выше. Однако, если вы запускаете сборки Gradle из командной строки, вам необходимо установить minSdkVersion равным 21 или выше, чтобы включить pre-dexing.
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, вы можете предоставить отдельный файл манифеста для каждого варианта, чтобы только файл для уровня API 20 и ниже изменял имя тега <application> . Вы также можете создать отдельный подкласс Application для каждого варианта, чтобы только подкласс для уровня API 20 и ниже расширял класс MultiDexApplication или вызывал MultiDex.install(this) .
Тестирование приложений Multidex
При написании инструментальных тестов для приложений Multidex дополнительная настройка не требуется, если вы используете инструментарий MonitoringInstrumentation или AndroidJUnitRunner . Если вы используете другой Instrumentation , необходимо переопределить его метод onCreate() следующим кодом:
Котлин
fun onCreate(arguments: Bundle) { MultiDex.install(targetContext) super.onCreate(arguments) ... }
Java
public void onCreate(Bundle arguments) { MultiDex.install(getTargetContext()); super.onCreate(arguments); ... }