Использование 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, которая будет упакована внутри приложения
  • Все ресурсы библиотеки и т. Д. Уже объединены, что означает необходимость разархивировать и снова сжать файл.

В скрипте сборки gradle

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

Другие вопросы по тегам