Post_20150120_AndroidDEX_Header

If you are an Android developer reading this, chances are you’ve at least 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. If your source code and all those cool libraries that you are using exceed this limit – kaboom!

UNEXPECTED TOP-LEVEL EXCEPTION:

com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536 
   at com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:502) 
   at com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:277) 
   at com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:491) 
   at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:168) 
   at com.android.dx.merge.DexMerger.merge(DexMerger.java:189) 
   at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:454) 
   at com.android.dx.command.dexer.Main.runMonoDex(Main.java:302) 
   at com.android.dx.command.dexer.Main.run(Main.java:245) 
   at com.android.dx.command.dexer.Main.main(Main.java:214) 
   at com.android.dx.command.Main.main(Main.java:106)

For more detailed information about the issue, read this detailed post on the topic.

Not to be deterred, the amazing Android developer community has come up with a few solutions, like this one from dmarcato, and this one from casidiablo. They work, but required you to do some serious acrobatics.

Eventually, Google decided to release an official solution for this in the form of the MultiDex Support Library which was first announced back in Oct ‘14 and Gradle support was added in v0.14.0 of the Android Gradle plugin a few weeks later.

Using the MultiDex Support Library

If you are using Android Studio, the process is very straight-forward. If you are not, I highly recommend migrating, as Google may soon drop support for the Eclipse ADT plugin and the old Ant based build system.

Step 1

Add the dependency for the MultiDex support library in your build.gradle

dependencies { 
... 
   compile 'com.android.support:multidex:' 
   ... 
}

Step 2

Enable multi-dexing by setting the multiDexEnabled flag in the buildType or productFlavor section of your gradle configuration.

defaultConfig { 
   ... 
multiDexEnabled true 
... 
}

Now depending on your project, you have 3 options:

If you haven’t created your own Application class, simply declare android.support.multidex.MultiDexApplication as your application class in AndroidManifest.xml

<application <="" br="">   .... 
   android:name="android.support.multidex.MultiDexApplication" 
   ... 

If you already have your own Application class, make it extend android.support.multidex.MultiDexApplication instead of android.app.Application

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); 
   } 
}

Whichever you choose, each option 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, when you build the app, Gradle will generate multiple dex files and generate an APK that you can successfully run on a device/emulator.

Post_20150120_AndroidDEX_code

You can see it in action in this sample app on Github.

Caveats

Out of memory issues

For projects with lots of dependencies, the build process may fail with the following error –

Error:Execution failed for task ':app:dexDebug'. 
... 
   Error Code: 
      3 
   Output: 
      UNEXPECTED TOP-LEVEL ERROR: 
      java.lang.OutOfMemoryError: GC overhead limit exceeded 
         at com.android.dx.cf.cst.ConstantPoolParser.parse0(ConstantPoolParser.java:326) 
...

To fix it, set the following dex options in the ‘android’ closure –

dexOptions { 
   incremental true 
   javaMaxHeapSize "4g" 
}

Slow application startup

Based on our experience with this support library, it works fine in most cases. However, it has a big impact on application launch time with large apps on some devices like the Kindle Fire. Loading all the classes on application launch simply takes a long time, which results in a black screen being displayed for several seconds, or in some cases, ANR errors.

More details, best-practices and limitations about MultiDex can be found here.

Conclusion

While the library fixes the DEX 64K problem in most cases, it should be treated as a last resort. Before attempting to use it, you should audit your project for unwanted dependencies and remove as much unused code as possible using ProGuard. If you do go ahead and use it, make sure you test your app on older devices.