Dex, multiple dex, Multidex
Running Scala in Android app
Because Scala language has powerful features for collection processing, Optionals support, pattern matching, type inference, I wanted to try out how mature is support with Android development… More about that maybe next time.To mention I am using the Android Studio with Gradle.
Before I even got into it, I quickly run into two problems at setting up the project. The first one is that compiling time is now 3 times longer. It can take more than a minute, and can be very annoying when you need to recompile the project because you want to see some small change made in the UI.
Second obstacle was that I couldn’t even deploy the app. When I have build the solution, I got the following DexIndexOverflowException exception.
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:283)
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:303)
at com.android.dx.command.dexer.Main.run(Main.java:246)
at com.android.dx.command.dexer.Main.main(Main.java:215)
at com.android.dx.command.Main.main(Main.java:106)
Error:Execution failed for task ':android:dexDebug'.
> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/bin/java'' finished with non-zero exit value 2
That means the application has reached a limit of the Android app build architecture. The first thing to spot is the magic number 65536. This is the exact same number of references that can be called by the single Dalvik Executable (dex) bytecod file. This happened after I included scala library which gets whole compiled into our Android application. This can be result of using big or many libraries in your project.
Multidex
Enabeling multidex seemed to be the easiest step. The application is then build in such a way that multiple bytecode files can be used. To enable multidex application package in the gradle android plugin with minimum SDK Build Tools 21.1 installed, just put multiDexEnabled true under the default configuration.
android {
defaultConfig {
...
multiDexEnabled true
}
Also the Application class, had to extend MultiDexApplication.
import android.support.multidex.MultiDexApplication;
public class ClientApplication extends MultiDexApplication {
...
}
When we apply these steps, the Android build tools constructs main dex (classes.dex) and additional ones (classes2.dex, classes3.dex) as needed. At the end of build process they will be packed in an APK file ready for installation.
Still I get error on installing the application on Simulator with API 10, this time LinearAlloc:
Failure [INSTALL_FAILED_DEXOPT]
LinearAlloc exceeded capacity (5242880), last=4396
09-23 14:43:51.008 558-558/? E/dalvikvm﹕ VM aborting
09-23 14:43:51.012 118-118/? W/installd﹕ DexInv: --- END '/data/app/com.rivancic.android.client.apk' ---
status=0x000b, process failed
09-23 14:43:51.012 118-118/? E/installd﹕ dexopt failed on '/data/dalvik-cache/data@[email protected]@classes.dex' res = 11
09-23 14:43:51.012 182-198/system_process W/PackageManager﹕ Package couldn't be installed in /data/app/com.rivancic.android.client.apk
I even get errors installing it on API 15 tablet. I managed to run it on API 16+ With Multidex enabled. You get in this mess if you do not read the documentation which says. Applications that use multidex may not start on devices that run versions of the platform earlier than Android 4.0 (API level 14) due to a Dalvik linearAlloc bug (Issue 22586)… The allocation limit was increased in Android 4.0 (API level 14), but apps may still run into this limit on Android versions prior to Android 5.0 (API level 21).
Handy tools
KeepSafe/dexcount-gradle-plugin Gradle plugin that lets you analyze your apk for reference count.
APK Method count The graphical online tool that does the same.
Methods Count Specify the library and get method count and size.
Sources
Building Apps with Over 65K Methods
[saturday06 - gradle scala android plugin] (https://github.com/saturday06/gradle-android-scala-plugin)