У меня довольно большое приложение для Android, которое опирается на многие библиотечные проекты. Компилятор Android имеет ограничение в 65536 методов на файл .dex, и я превышаю это число.
По сути, есть два пути, которые вы можете выбрать (по крайней мере, о которых я знаю), когда вы достигнете предела метода.
1) Уменьшите код
2) Создайте несколько файлов dex ( см. Это сообщение в блоге )
Я изучил оба и попытался выяснить, из-за чего мой счет методов стал таким большим. Google Drive API занимает самый большой кусок с зависимостью Guava - более 12000. Общее количество библиотек для Drive API v2 превышает 23 000!
У меня вопрос, как вы думаете, что мне делать? Следует ли мне удалить интеграцию с Google Диском как функцию моего приложения? Есть ли способ уменьшить API (да, я использую proguard)? Должен ли я пойти по маршруту с несколькими dex (что выглядит довольно болезненно, особенно со сторонними API)?
источник
apk
форме? Я лично хотел бы видеть интеграцию с DriveОтветы:
Похоже, что Google наконец-то реализовал обходной путь / исправление для превышения ограничения в 65 КБ для dex-файлов.
См .: Создание приложений с использованием более 65K методов.
Вам по-прежнему следует избегать превышения лимита метода в 65 КБ, активно используя proguard и просматривая свои зависимости.
источник
вы можете использовать для этого библиотеку поддержки multidex, чтобы включить multidex
1) включить его в зависимости:
dependencies { ... compile 'com.android.support:multidex:1.0.0' }
2) Включите его в своем приложении:
defaultConfig { ... minSdkVersion 14 targetSdkVersion 21 .... multiDexEnabled true }
3) если у вас есть класс приложения для вашего приложения, переопределите метод attachBaseContext следующим образом:
package ....; ... import android.support.multidex.MultiDex; public class MyApplication extends Application { .... @Override protected void attachBaseContext(Context context) { super.attachBaseContext(context); MultiDex.install(this); } }
4) если вы не имеют приложения класса для вашего приложения , то зарегистрировать android.support.multidex.MultiDexApplication в качестве приложения в вашем файле манифеста. как это:
<application ... android:name="android.support.multidex.MultiDexApplication"> ... </application>
и должно работать нормально!
источник
Play Services
6.5+ помогает: http://android-developers.blogspot.com/2014/12/google-play-services-and-dex-method.html...
Это хорошие новости, например, для простой игры вам, вероятно, понадобится только
base
,games
и, возможноdrive
.источник
В версиях сервисов Google Play до 6.5 вам нужно было скомпилировать весь пакет API в свое приложение. В некоторых случаях это затрудняло поддержание количества методов в вашем приложении (включая API фреймворка, библиотечные методы и ваш собственный код) в пределах 65 536.
Начиная с версии 6.5, вместо этого вы можете выборочно компилировать API сервисов Google Play в свое приложение. Например, чтобы включить только API Google Fit и Android Wear, замените следующую строку в файле build.gradle:
compile 'com.google.android.gms:play-services:6.5.87'
с этими строками:
compile 'com.google.android.gms:play-services-fitness:6.5.87' compile 'com.google.android.gms:play-services-wearable:6.5.87'
для получения дополнительной информации вы можете нажать здесь
источник
Используйте proguard, чтобы облегчить ваш apk, поскольку неиспользуемые методы не будут включены в вашу окончательную сборку. Дважды проверьте, что у вас есть следующее в вашем конфигурационном файле proguard, чтобы использовать proguard с guava (приношу свои извинения, если у вас уже есть это, на момент написания этого не было известно):
# Guava exclusions (http://code.google.com/p/guava-libraries/wiki/UsingProGuardWithGuava) -dontwarn sun.misc.Unsafe -dontwarn com.google.common.collect.MinMaxPriorityQueue -keepclasseswithmembers public class * { public static void main(java.lang.String[]); } # Guava depends on the annotation and inject packages for its annotations, keep them both -keep public class javax.annotation.** -keep public class javax.inject.**
Кроме того, если вы используете ActionbarSherlock, переход на библиотеку поддержки appcompat v7 также значительно сократит количество ваших методов (исходя из личного опыта). Инструкции расположены:
источник
Warning: butterknife.internal.ButterKnifeProcessor: can't find superclass or interface javax.annotation.processing.AbstractProcessor
когда бежал./gradlew :myapp:proguardDevDebug
Вы можете использовать Jar Jar Links для сжатия огромных внешних библиотек, таких как Google Play Services (методы 16K!)
В вашем случае вы просто рипы все от Google Play Services банки , за исключением
common
internal
иdrive
суб-пакеты.источник
Для пользователей Eclipse, не использующих Gradle, есть инструменты, которые разбивают банку Google Play Services и перестраивают ее только с теми частями, которые вам нужны.
Я использую strip_play_services.sh от dextorer .
Может быть трудно точно определить, какие службы включить, потому что есть некоторые внутренние зависимости, но вы можете начать с малого и добавить в конфигурацию, если окажется, что необходимые вещи отсутствуют.
источник
Я думаю, что в долгосрочной перспективе лучшим способом будет разбить ваше приложение на несколько dex.
источник
Поддержка Multi-dex
будетофициальным решением этой проблемы. Подробности см. В моем ответе .источник
Если не использовать multidex, который сильно замедляет процесс сборки. Вы можете сделать следующее. Как упоминалось yahska, используйте определенную библиотеку службы Google Play. В большинстве случаев требуется только это.
compile 'com.google.android.gms:play-services-base:6.5.+'
Вот все доступные пакеты. Выборочная компиляция API в ваш исполняемый файл.
Если этого будет недостаточно, вы можете использовать скрипт Gradle. Поместите этот код в файл strip_play_services.gradle
def toCamelCase(String string) { String result = "" string.findAll("[^\\W]+") { String word -> result += word.capitalize() } return result } afterEvaluate { project -> Configuration runtimeConfiguration = project.configurations.getByName('compile') println runtimeConfiguration ResolutionResult resolution = runtimeConfiguration.incoming.resolutionResult // Forces resolve of configuration ModuleVersionIdentifier module = resolution.getAllComponents().find { it.moduleVersion.name.equals("play-services") }.moduleVersion def playServicesLibName = toCamelCase("${module.group} ${module.name} ${module.version}") String prepareTaskName = "prepare${playServicesLibName}Library" File playServiceRootFolder = project.tasks.find { it.name.equals(prepareTaskName) }.explodedDir def tmpDir = new File(project.buildDir, 'intermediates/tmp') tmpDir.mkdirs() def libFile = new File(tmpDir, "${playServicesLibName}.marker") def strippedClassFileName = "${playServicesLibName}.jar" def classesStrippedJar = new File(tmpDir, strippedClassFileName) def packageToExclude = ["com/google/ads/**", "com/google/android/gms/actions/**", "com/google/android/gms/ads/**", // "com/google/android/gms/analytics/**", "com/google/android/gms/appindexing/**", "com/google/android/gms/appstate/**", "com/google/android/gms/auth/**", "com/google/android/gms/cast/**", "com/google/android/gms/drive/**", "com/google/android/gms/fitness/**", "com/google/android/gms/games/**", "com/google/android/gms/gcm/**", "com/google/android/gms/identity/**", "com/google/android/gms/location/**", "com/google/android/gms/maps/**", "com/google/android/gms/panorama/**", "com/google/android/gms/plus/**", "com/google/android/gms/security/**", "com/google/android/gms/tagmanager/**", "com/google/android/gms/wallet/**", "com/google/android/gms/wearable/**"] Task stripPlayServices = project.tasks.create(name: 'stripPlayServices', group: "Strip") { inputs.files new File(playServiceRootFolder, "classes.jar") outputs.dir playServiceRootFolder description 'Strip useless packages from Google Play Services library to avoid reaching dex limit' doLast { def packageExcludesAsString = packageToExclude.join(",") if (libFile.exists() && libFile.text == packageExcludesAsString && classesStrippedJar.exists()) { println "Play services already stripped" copy { from(file(classesStrippedJar)) into(file(playServiceRootFolder)) rename { fileName -> fileName = "classes.jar" } } } else { copy { from(file(new File(playServiceRootFolder, "classes.jar"))) into(file(playServiceRootFolder)) rename { fileName -> fileName = "classes_orig.jar" } } tasks.create(name: "stripPlayServices" + module.version, type: Jar) { destinationDir = playServiceRootFolder archiveName = "classes.jar" from(zipTree(new File(playServiceRootFolder, "classes_orig.jar"))) { exclude packageToExclude } }.execute() delete file(new File(playServiceRootFolder, "classes_orig.jar")) copy { from(file(new File(playServiceRootFolder, "classes.jar"))) into(file(tmpDir)) rename { fileName -> fileName = strippedClassFileName } } libFile.text = packageExcludesAsString } } } project.tasks.findAll { it.name.startsWith('prepare') && it.name.endsWith('Dependencies') }.each { Task task -> task.dependsOn stripPlayServices } project.tasks.findAll { it.name.contains(prepareTaskName) }.each { Task task -> stripPlayServices.mustRunAfter task }
}
Затем примените этот скрипт в своем build.gradle, например
apply plugin: 'com.android.application' apply from: 'strip_play_services.gradle'
источник
Если вы используете сервисы Google Play, вы можете знать, что он добавляет более 20 тысяч методов. Как уже упоминалось, Android Studio имеет возможность модульного включения определенных сервисов, но пользователи, застрявшие с Eclipse, должны взять модульность в свои руки :(
К счастью, есть сценарий оболочки, который упрощает эту работу. Просто распакуйте в каталог jar сервисов Google Play, отредактируйте предоставленный файл .conf по мере необходимости и выполните сценарий оболочки.
Пример его использования здесь .
источник
Как он и сказал, я заменяю
compile 'com.google.android.gms:play-services:9.0.0'
только те библиотеки, которые мне нужны, и это сработало.источник