Использование Gradle для разделения внешних библиотек в разделенных файлах dex для решения ограничения Android Dalvik 64k
Есть ли proper/easy
способ решить ограничение методов 64k с помощью Gradle?
Я имею в виду некоторую пользовательскую задачу Gradle, использующую предварительно дексированные банки для создания отдельных файлов dex, вместо одного classes.dex
,
Спасибо
Иван
Текущее состояние
В настоящее время я борюсь с GMS: в нее добавлено 20 000 методов для использования Analytics. Я использую Proguard, чтобы раздеть то, что не нужно, но все же... 72k методов и подсчета...
Я могу разделить classes.dex
в двух файлах, используя dx
параметр --multi-dex. Я добился ручного редактирования
sdk/build-tools/android-4.4W/dx
и редактируем последнюю строку следующим образом:
exec java $javaOpts -jar "$jarpath" --multi-dex "$@"
Мой файл APK теперь содержит __classes.dex__ and __classes2.dex__
,
Я пытаюсь динамически загрузить второй файл несколькими способами:
К сожалению, до сих пор не повезло. Я действительно надеюсь, что некоторые гуру Google/Facebook/Square смогут найти правильное решение.
4 ответа
Обновление для плагина Android Gradle 2.2.0: невозможно получить доступ к dex
задача больше, но взамен additionalParameters
был введен как часть dexOptions
, Используйте это как
android {
dexOptions {
additionalParameters += '--minimal-main-dex'
// additionalParameters += '--main-dex-list=$projectDir/<filename>'.toString()
// additionalParameters += '--set-max-idx-number=55000'
}
}
Обновление для плагина Android Gradle 0.14.0: теперь есть прямая поддержка мульти-декс через новый multiDexEnabled true
директива (требуются инструменты сборки 21.1.0, поддержка 8-й версии репозитория и Android Studio 0.9).
Оригинальный ответ: С тех пор, как Gradle Android плагин 0.9.0 вы действительно можете пройти --multi-dex
в dx
добавив это в приложение build.gradle
файл:
afterEvaluate {
tasks.matching {
it.name.startsWith('dex')
}.each { dx ->
if (dx.additionalParameters == null) {
dx.additionalParameters = ['--multi-dex']
} else {
dx.additionalParameters += '--multi-dex'
}
// Add more additional parameters like this:
dx.additionalParameters += '--main-dex-list=class-list.txt'
dx.additionalParameters += '--minimal-main-dex'
}
}
До сих пор для создания он несколько файлов dex. Чтобы фактически использовать несколько файлов dex, взгляните на https://github.com/casidiablo/multidex (который является форком будущей библиотеки поддержки MultiDex от Google).
В случае, если GMS была вашей проблемой, и вы используете Gradle
Начиная с версии 6.5 gms вы можете выбирать отдельные библиотеки API
например, чтобы включить только API Карт:
compile 'com.google.android.gms:play-services-maps:6.5.87'
и вот полный список:
com.google.android.gms:play-services-base:6.5.87
com.google.android.gms:play-services-ads:6.5.87
com.google.android.gms:play-services-appindexing:6.5.87
com.google.android.gms:play-services-maps:6.5.87
com.google.android.gms:play-services-location:6.5.87
com.google.android.gms:play-services-fitness:6.5.87
com.google.android.gms:play-services-panorama:6.5.87
com.google.android.gms:play-services-drive:6.5.87
com.google.android.gms:play-services-games:6.5.87
com.google.android.gms:play-services-wallet:6.5.87
com.google.android.gms:play-services-identity:6.5.87
com.google.android.gms:play-services-cast:6.5.87
com.google.android.gms:play-services-plus:6.5.87
com.google.android.gms:play-services-appstate:6.5.87
com.google.android.gms:play-services-wearable:6.5.87
com.google.android.gms:play-services-all-wear:6.5.87
Я поддерживаю https://github.com/creativepsyco/secondary-dex-gradle/ и я n00b Gradle, поэтому я выбрал путь сценариев BASH, хотя думаю, что это можно сделать непосредственно в файле сборки. ИЛИ может быть реорганизован для запуска в качестве плагина, я мог бы сделать это, когда я согласен с Gradle. Вот причина моей логики.
Чтобы понять, как разделить DEX, вы должны знать порядок задач системы сборки. Если вы используете gradle, вы должны знать, что в цикле сборки есть ряд задач.
Например:
:sdk:processReleaseJavaRes UP-TO-DATE
:sdk:packageReleaseJar
:sdk:compileReleaseNdk UP-TO-DATE
:sdk:packageReleaseJniLibs UP-TO-DATE
:sdk:packageReleaseLocalJar UP-TO-DATE
:sdk:packageReleaseRenderscript UP-TO-DATE
:sdk:packageReleaseResources UP-TO-DATE
:sdk:bundleRelease
:app:prepareComAndroidSupportAppcompatV71910Library UP-TO-DATE
:app:prepareComFacebookAndroidFacebook3141Library UP-TO-DATE
:app:prepareDebugDependencies
:app:compileDebugAidl UP-TO-DATE
:app:compileDebugRenderscript UP-TO-DATE
:app:generateDebugBuildConfig UP-TO-DATE
:app:generateDebugAssets UP-TO-DATE
:app:mergeDebugAssets UP-TO-DATE
:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources UP-TO-DATE
:app:processDebugManifest UP-TO-DATE
:app:processDebugResources UP-TO-DATE
:app:generateDebugSources UP-TO-DATE
:app:compileDebugJava
:app:preDexDebug
:app:dexDebug
:app:processDebugJavaRes UP-TO-DATE
:app:validateReleaseConfigSigning
:app:packageDebug
:app:zipalignDebug
:app:assembleDebug
Для того, чтобы выполнять Dexing, вы должны иметь возможность вставлять свои пользовательские задачи между задачами dex* и process*. Если вы можете сделать это, тогда Multiple DEXing станет легким.
Сценарий Bash здесь, по сути, делает это, если вы изучите задачу отладки, то в основном:
- Получите файл Library Jar на dex, обычно он зависит от конкретной сборки и существует в
exploded-aar
папку для библиотек Android и запустите на ней инструмент DEX - Скопируйте это в папку assets, которая существует в последней папке libs, которая будет упакована внутри приложения
- Все ресурсы библиотеки и т. Д. Уже объединены, что означает необходимость разархивировать и снова сжать файл.
// For Debug simply remove the library from getting dex and create it
//----------------------- Extra Debug Step ----------------//
def libraryFiles = new ArrayList<?>()
def secondaryFile = new ArrayList<?>()
variant.dex.libraries.each {
File file ->
if (!file.absolutePath.contains("lib/unspecified/classes.jar")) {
libraryFiles.add(file)
} else {
secondaryFile.add(file)
}
}
variant.dex.libraries = libraryFiles
//----------------------- Extra Debug Step ----------------//
packagingTask.dependsOn variant.javaCompile
}
Это вручную удаляет библиотеку от получения dexed, так что она может быть сгенерирована через скрипт bash.
Я думаю, что вы можете определить дексинг в процессе релиза таким же образом. Еще одна важная вещь, на которую стоит обратить внимание, это то, что Задача Proguard контролируется плагином для Android, и вы не можете ничего с этим поделать. Проблема с правилами Proguard:
- Каждый проход proguard индивидуален, мы не хотим оказаться в ситуации, когда наши 2 DEX имеют разные отображения proguard
- Это оставляет нас в ситуации, когда мы не можем защитить наши библиотеки, но это не очень желательно.
- Должен сгенерировать файл dex после proguard, чтобы убедиться, что сопоставления одинаковы. Gradle не поддерживает объединение ресурсов после Proguard (мы хотим поместить файлы dex в папку assets)
Другой важный фрагмент кода находится в SecondaryDex.java, который по существу загружает второй файл dex и внедряет путь файла DEX в путь класса времени выполнения. Вы можете оптимизировать это и просто вводить путь вместо чтения DEX-файла при каждом возобновлении работы приложения.
Я провел дополнительный эксперимент Dex в Google Play Services (который добавляет методы 20 КБ) и смог разделить его на отдельный файл DEX. Таким образом, мой основной dex-файл не подвержен распространению в сервисах Google Play.
Чтобы понять, как работает цикл задач Gradle, вы можете обратиться к источнику BasePlugin.groovy, вы можете увидеть, что некоторые аспекты трудно контролировать, пока не будет подходящего API для доступа к вариантным объектам и задачам сборки.
Пример разделения проекта и загрузки различных файлов dex можно найти здесь:
https://code.google.com/p/android-custom-class-loading-sample/
РЕДАКТИРОВАТЬ: для Gradle у вас уже есть ответ
Загрузка пользовательских классов в Dalvik с Gradle (новая система сборки Android)