Использование Gradle для разделения внешних библиотек в разделенных файлах dex для решения ограничений метода Android Dalvik 64k

Есть ли способ proper/easy решить предел 64k-методов, используя Gradle?

Я имею в виду некоторые пользовательские задачи Gradle для использования предварительно дексированных банок для создания разделенных файлов dex вместо одного classes.dex.

Спасибо

Иван

Текущее состояние

В настоящее время я борюсь с GMS: он использует методы 20k для использования Analytics. Я использую Proguard, чтобы вырезать то, что вам не нужно, но все же... 72k методы и подсчет...

Я могу разделить classes.dex на два файла с помощью параметра dx - multi-dex. Я достиг этого вручную.

sdk/build-tools/android-4.4W/dx

и отредактируйте последнюю строку следующим образом:

exec java $javaOpts -jar "$jarpath" --multi-dex "[email protected]"

Мой файл APK теперь содержит __classes.dex__ and __classes2.dex__.

Я пытаюсь динамически загружать второй файл несколькими способами:

К сожалению, до сих пор не повезло. Я действительно надеюсь, что Google/Facebook/Square гуру может обеспечить правильное решение.

Ответ 1

Обновление для Android Gradle plugin 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 (требуется build-tools 21.1.0, версия 8 репозитория поддержки, и Android Studio 0.9).

Оригинальный ответ: С момента создания Gradle Android-плагина 0.9.0 вы действительно можете передать --multi-dex в dx, добавив это к вашему приложению build.gradle file:

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).

Ответ 2

В случае, если gms была вашей проблемой, и вы используете gradle

Начиная с версии gms 6.5 вы можете выбрать отдельные библиотеки 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

Ответ 4

Я поддерживаю https://github.com/creativepsyco/secondary-dex-gradle/, и я являюсь gradle n00b, поэтому я выбрал путь к BASH скриптам, хотя Я думаю, что это можно сделать непосредственно в файле сборки. OR может быть реорганизован для запуска в качестве плагина, я могу сделать это, когда я согласен с условиями 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 script здесь по существу делает это, если вы изучите задачу отладки, это будет в основном:

  • Загрузите файл Jar библиотеки в dex, как правило, он специфичен для сборки и существует в папке exploded-aar для Android-библиотек и запустите инструмент DEX на нем.
  • Скопируйте это в папку с ресурсами, которая существует в последней папке libs, которая будет упакована внутри приложения.
  • Все ресурсы библиотеки и т.д. уже объединены, что означает необходимость распаковать и снова закрепить файл.

В gradle build script

 // 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
            }

Это вручную удаляет библиотеку из-за того, что она может быть сгенерирована, поэтому она может быть сгенерирована с помощью BASH script.

Я думаю, что вы можете разобраться с dexing во время процесса выпуска таким же образом. Другая важная вещь, которую следует отметить, заключается в том, что Proguard Task управляется плагином android gradle, и вы не можете многое изменить. Проблема с правилами Proguard:

  • Каждый проход proguard отличается, мы не хотим заканчиваться в ситуации, когда наши 2 DEXes имеют разные сопоставления proguard
  • Это оставляет нас в ситуации, когда мы не можем защищать наши библиотеки, но это не очень желательно.
  • Должен генерировать файл dex после proguard, чтобы убедиться, что сопоставления совпадают. gradle не поддерживает объединение активов после Proguard (мы хотим поместить файлы dex в папку с ресурсами)

Другой важный фрагмент кода находится в SecondaryDex.java, который по существу загружает второй файл dex и вводит путь к файлу DEX в runtime class path. Вы можете оптимизировать это и просто вводить путь вместо чтения файла DEX каждый раз, когда приложение возобновляется.

Я сделал дополнительный эксперимент Dex в Google Play Services (который добавляет 20K-методов) и смог разделиться в отдельный файл DEX. Таким образом, мой основной файл dex не затронут раздуванием в сервисах Google Play.

Чтобы понять, как работает цикл задач gradle, вы можете ссылаться на источник BasePlugin.groovy, вы можете видеть, что это сложно для управления некоторыми аспектами, пока не будет надлежащего API для доступа к вариантным объектам и задачам сборки.