DEX’s 64k limit is not a problem anymore, well almost...

If you are an Android developer reading this, chances are that you’ve heard of the dreaded 64k method limit in Dalvik. In a nutshell, the issue is that in a DEX file, you can reference a very large number of methods, but you can only invoke the first 65,536 of them because that’s all the room you have in the method invocation set. So if your source code and all those cool libraries that you are using, when combined, exceed this limit - kaboom!

Dex Exception

For more detailed information about why this happens, read this awesome post on the topic.

Not to be deterred, the amazing Android developer community has come up with some solutions to this problem like this and this. They work, but require you to do some serious acrobatics to get them working correctly. Last week, a Googler, Ian Lake posted this on G+ and my excitement knew no bounds.

Ian Lake's post

Having faced this issue myself several times so far, I had to try it out. So here’s what we need to do —

Import android-support-multidex.jar from sdk/extras/android/support/multidex/library/libs into your application.

Now based on your project, you have 3 options:

  1. If you’ve not created your own Application class, simply declare android.support.multidex.MultiDexApplication as your application class in AndroidManifest.xml
  2. If you already have your own Application class, make it extend android.support.multidex.MultiDexApplication
  3. If your Application class is extending some other class and you don’t want to or can’t change it, override attachBaseContext() as shown below:
public class MyApplication extends FooApplication {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }
}

Irrespective of which option you choose, what happens is that it generates multiple dex files instead of a single massive one and also takes care of loading all the classes in each one of them at run-time. Neat, right?

Now you must be assuming you can just do this and you can go back to watching cat videos on the internet, well sorry, but that’s not gonna happen. If you run your app after making these changes, you’ll see the same error again.

That’s because, as of the time of writing this, there is no official way of enabling the creation of multiple dex files in Gradle. It is supposedly coming in “a few weeks” but we all know how that works.

11/8/14: Official support is now available, if you are in a hurry, you can jump to end of this page for instructions on how to use it.

In the meantime, since Gradle Android plugin v0.9.0, we can actually pass the -—multi-dex flag to dex to make it generate multiple dex files. To do so, we just need to add the following lines to the app’s build.gradle.

afterEvaluate {
    tasks.matching {
        it.name.startsWith('dex')
    }.each { dx ->
        if (dx.additionalParameters == null) {
            dx.additionalParameters = ['--multi-dex']
        } else {
            dx.additionalParameters += '--multi-dex'
        }
    }
}

If we try to build the app now, we’ll see an error again, though a new one, but that’s okay, at least we are making progress.

Dex top level exception

This is because multi-dex mode is currently incompatible with pre-dexing library projects. Pre-dexing is a means of speeding up incremental builds by, well, pre-dexing the dependencies of each module so that the dex task can be made to re-dex only what’s changed.

To make multi-dex mode work, we’ll have to disable pre-dexing. Doing so is pretty straight-forward. Just add the following lines in your root build.gradle.

project.ext.preDexLibs = !project.hasProperty('disablePreDex')

subprojects {
    project.plugins.whenPluginAdded { plugin ->
        if ("com.android.build.gradle.AppPlugin".equals(plugin.class.name)) {
            project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
        } else if ("com.android.build.gradle.LibraryPlugin".equals(plugin.class.name)) {
            project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
        }
    }
}

Now to build your project, do a ./gradlew clean assemble -PdisablePreDex and it will generate multiple dex files and create an APK that you can successfully run on a device/emulator.

This is just a stop-gap measure to make multi-dex work until the official gradle support is released. However, switching from this to the official solution should be a piece of cake, which is why I think it’s a good idea to use it for now. You can see it working in a sample project on GitHub.

Using the official solution

Google added multidex support in v0.14.0 of the Android Gradle plugin, so now things are very straight-forward.

  1. Ensure that you are using v21.10 of the Android build tools
  2. Update your application class as described earlier
  3. Modify your gradle config to include the support library and enable multidex
...
defaultConfig {
     ...
     multiDexEnabled true
}
...

You can set the multiDexEnabled attribute in buildType or productFlavor sections as well. Now when you run the application, Gradle will generate multiple dex files and build an APK that you can successfully run on a device/emulator.

Multiple Dex files

For more information, you can read the official guide, limitations and also check out this sample.

Now where’s that tab with the cat videos.