19

So I am wondering why I encounter the 64k dex method limit when trying to run my app on android versions older than lollipop, when it runs just fine on the more recent versions.

Could it be, because the support libraries are actually being referenced when running on the older versions?

This is my gradle:

    apply plugin: 'com.android.application'

    android {
        compileSdkVersion 23
        buildToolsVersion '23.0.2'
    lintOptions {
        checkReleaseBuilds true
        // Or, if you prefer, you can continue to check for errors in release builds,
        // but continue the build even when errors are found:
        abortOnError false
    }

    defaultConfig {
        applicationId "com.domain.myapp"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 27
        versionName "1.2"

        // Vector compat
        vectorDrawables.useSupportLibrary = true 
    }

    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}

dependencies {

    compile files('libs/commons-io-2.4.jar')
    compile files('libs/activation.jar')
    compile files('libs/additionnal.jar')
    compile files('libs/mail.jar')
    compile project(':libraries:preferencefragment')

    // Gmail API
    compile('com.google.api-client:google-api-client-android:1.20.0') {
        exclude group: 'org.apache.httpcomponents'
    }
    compile('com.google.apis:google-api-services-gmail:v1-rev29-1.20.0') {
        exclude group: 'org.apache.httpcomponents'
    }

    // Play Services
    compile 'com.google.android.gms:play-services-location:8.4.0'
    compile 'com.google.android.gms:play-services-maps:8.4.0'
    compile 'com.google.android.gms:play-services-ads:8.4.0'
    compile 'com.google.android.gms:play-services-analytics:8.4.0'
    compile 'com.google.android.gms:play-services-identity:8.4.0'

    // Support libraries
    compile 'com.android.support:support-v4:23.3.0'
    compile 'com.android.support:appcompat-v7:23.3.0'
    compile 'com.android.support:cardview-v7:23.3.0'
    compile 'com.android.support:design:23.3.0'

    compile 'com.github.bumptech.glide:glide:3.6.1'
    compile 'com.anjlab.android.iab.v3:library:1.0.20'
    compile 'com.sothree.slidinguppanel:library:2.0.3'
    compile 'com.commit451:PhotoView:1.2.5'

    compile('com.github.afollestad.material-dialogs:core:0.8.5.7@aar') {
        transitive = true
    }
}

EDIT:

To clarify: This happens when I try to run the app on an emulator running e.g. KitKat API 19

Crash logs from the gradle console:

...
:App:generateDebugInstantRunAppInfo
:App:transformClassesWithDexForDebug
AGPBI: {"kind":"error","text":"The number of method references in a .dex file cannot exceed 64K.\nLearn how to resolve this issue at https://developer.android.com/tools/building/multidex.html","sources":[{}],"original":"UNEXPECTED TOP-LEVEL EXCEPTION:\ncom.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536\n\tat com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:484)\n\tat com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:261)\n\tat com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:473)\n\tat com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:161)\n\tat com.android.dx.merge.DexMerger.merge(DexMerger.java:188)\n\tat com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:504)\n\tat com.android.dx.command.dexer.Main.runMonoDex(Main.java:334)\n\tat com.android.dx.command.dexer.Main.run(Main.java:277)\n\tat com.android.dx.command.dexer.Main.main(Main.java:245)\n\tat com.android.dx.command.Main.main(Main.java:106)\n","tool":"Dex"}

 FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':App:transformClassesWithDexForDebug'.
> com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/bin/java'' finished with non-zero exit value 2

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Crash message from the Message window:

:App:transformClassesWithDexForDebug
Error:The number of method references in a .dex file cannot exceed 64K.
Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
Error:Execution failed for task ':App:transformClassesWithDexForDebug'.
> com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/bin/java'' finished with non-zero exit value 2
Jakob Harteg
  • 9,587
  • 15
  • 56
  • 78
  • 2
    I would do some verbose logging in your gradle build to find the actual dx commands that are being run. This should give a hint as to what's different when you compile for newer vs. older apis. It might be the support library as you suspect - I'm not really sure. – JesusFreke Apr 11 '16 at 22:11
  • 2
    Can you show the exact failure message? If your app is running on a device then you're not hitting the classic 64K method limit. – fadden Apr 11 '16 at 22:23
  • Another option would be to go ahead and enable multi-dex, and then use something like baksmali to inspect the 2 apks to see what classes are being included. You might be able to see some additional support libraries classes in the apk built for the older sdk. – JesusFreke Apr 11 '16 at 23:38
  • Related to @fadden's question: Is this an error that occurs when you're actually *running* the app on the device? Or an error when building the apk for different target sdk levels? – JesusFreke Apr 12 '16 at 00:13
  • Thank you all for your engangement. @fadden I added crash logs – Jakob Harteg Apr 12 '16 at 20:47
  • Also you should try to shrink Google Play Services library as described in https://developers.google.com/android/guides/setup#add_google_play_services_to_your_project. – CoolMind Jul 12 '16 at 12:53
  • 1
    @CoolMind if you mean only include the Google Servies APIs that I actually use, then I have of course done so already. But thanks for the suggestions nevertheless. Cheers – Jakob Harteg Jul 12 '16 at 19:16

4 Answers4

36

To answer your specific question is:

  • This method count limitation is on the DEX (Dalvik Executable) file.
  • A common workaround for this limitation is to have multiple DEX files.
  • Older versions of Android does not natively support multiple DEX files.
  • Starting from Lollipop the system supports it natively.

So that's why it fails on older devices but it works on newer devices. It has nothing to do with support libraries.

To add more useful content to the my answer:

But maybe what some might want to know is how to workaround it on older devices, please note that I used the word natively on my bullet points above. That means, that there're workarounds to make older platforms support it, but you have to code those workarounds into your app.

The workaround (and even the issue itself) is detailed explained on this Google guide: http://developer.android.com/tools/building/multidex.html

The base of it is:

  • add multidex to your dependencies compile 'com.android.support:multidex:+'
  • enable multiDex on your build script inside android->default config multiDexEnabled true
  • on the manifest make your application extends from MultidexApplication android:name="android.support.multidex.MultiDexApplication" or, if you need to sub-class Application for your own app, make your Application extends from it.
Budius
  • 39,391
  • 16
  • 102
  • 144
  • 4
    The 64k method failure isn't a runtime failure, but rather a build time failure. His build.gradle doesn't enable multidex, so this doesn't explain why he gets too many methods when building for an older sdk, but doesn't when building for a newer sdk. As you mentioned, the solution to this is to enable multi-dex, but that's not actually what his question is about. He's asking why it fails when building for an older sdk and not a newer sdk, not how to fix it. – JesusFreke Apr 12 '16 at 00:03
  • I have the same issue. But if I select my Nexus 6P (Android Nougat) and my old device (4.0.3) both as deploy target, the build is ok. The APK should not differ per device I choose to deploy on – Boy Aug 02 '16 at 13:07
2

Completely agree with @Budius. You must mention an application class in manifest. And have your application class extend MultiDexApplication.

There is one more thing needed. Calling attach base context in your MultiDexApplication class

public class MyApplicationClass extends MultiDexApplication... {
....

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(MyApplicationClass.this);
    }
   ...
}

This worked fine for me on pre and post lollipop devices.

drulabs
  • 3,071
  • 28
  • 35
  • 3
    His question isn't about fixing the error, but rather figuring out why it only occurs only for older sdk levels. There are plenty of other questions+answers that handle the method limit and how to fix it. – JesusFreke Apr 12 '16 at 02:45
2

The issue is occuring when using Instant Run and pre-lollipop devices!

https://code.google.com/p/android/issues/detail?id=208577 https://code.google.com/p/android/issues/detail?id=196809

Boy
  • 7,010
  • 4
  • 54
  • 68
  • Changing my instant run preferences and removing the check mark for "Enable instant run...." fixed my problem – Surekha Oct 04 '16 at 19:45
1

you should add multiDexEnabled = true in to your defaultConfig in gradle file.

FelixSFD
  • 6,052
  • 10
  • 43
  • 117