29 กุมภาพันธ์ 2559

[Android Code] DEX with Over 65K Methods problem and how to use MultiDex on Android



"The number of method references in a .dex file cannot exceed 64K"

        I think most android developer have been seen this error message before. This problem occurs when build android project to APK file.

        What does it mean? Why it happens? and how can we fix it?

An introduction to the .dex file

        In java, when .java was compiled it will be .class file to execute with Java VM. But Android use Dalvik VM (pre-lollipop) instead of Java VM to execute .dex file that compressed from .class file because it compact and suitable for mobile device like as smartphone more than .class file.

        So when android application was compiled. The .java file will compiled to .class and compiled to .dex again. Then it will zip with resource file to .apk file.

        The limitation of .dex file is maximum method count support in this file is only 65,536 methods. These include the android framework, 3rd-party library and your code.

Exceed 65K Method that means method count more than 65,536 methods

        That's it! This problem because of limitation of .dex file. Sometime you may have seen 65K or 64K wording. That are same because 65,536 / 1.024 = 64,000.


        So it's normally if your big project stuck in this problem. If your project isn't large too much. I think you should check your project by follow this article.

Never seen this problem before?

        Let's try to add this dependencies in your test project.

compile 'com.google.android.gms:play-services:8.4.0'
compile 'com.android.support:design:23.1.1'
compile 'com.android.support:cardview-v7:23.1.1'
compile 'com.android.support:recyclerview-v7:23.1.1'
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:support-v4:23.1.1'
compile 'com.android.support:gridlayout-v7:23.1.1'
compile 'com.android.support:mediarouter-v7:23.1.1'
compile 'com.android.support:palette-v7:23.1.1'
compile 'com.android.support:preference-v7:23.1.1'
compile 'com.android.support:support-v13:23.1.1'
compile 'com.android.support:preference-v14:23.1.1'
compile 'com.android.support:preference-leanback-v17:23.1.1'
compile 'com.android.support:leanback-v17:23.1.1'
compile 'com.android.support:support-annotations:23.1.1'
compile 'com.android.support:customtabs:23.1.1'
compile 'com.android.support:percent:23.1.1'
compile 'com.squareup:otto:1.3.8'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.okhttp3:okhttp:3.0.1'

        Set minimum SDK version to 17 or higher and run it. You will see how this problem occurs.

How can we fix it?

        Most developer fix this problem by use MultiDex. In fact. It shouldn't always use MultiDex. but you can try to use another way to fix this problem before.

1. Reduce dependencies in your project

        Because of exceeding method count in your project. So you should fix the problem by reduce dependencies first.

        Check your project that contain with unused dependencies or not. If any dependencies can reduce or replace with another dependencies that compact than it, do it. For example, I found some projects are use Picasso and Glide together. Why don't you use one at all?

        Most problems that I found from another developers is use Google Play Services in them project like this.

compile 'com.google.android.gms:play-services:8.4.0'

        A pack of Google Play Services dependencies is very big, contain with 58,180 method!!

        So the best way is choosing only some dependencies in Google Play Services that you really want to use it.

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'

        These dependencies are use only 27,766 methods. It look still too much method count for me but it is inevitable, because these dependencies are required some important dependencies such as Android Support v4, Base Google Play Services and Support Annotation.


        Dependencies reducing by remove unnecessary dependencies is very important. You should do it first before use another way. Not only fix the exceed method count problem, but project building with gradle will be faster.

2. Set minimum SDK version to 21 or higher

        This isn't great way, if your application need to support pre-lollipop version. But if based-on lollipop version (May required any feature in Android Lollipop)

        Why it worked when change minimum SDK to 21? That's because of Android 5.0 or higher are use ART (Android Runtime) that supported MultiDex instead of Dalvik. So ART can support more than 65,536 methods.

        Inside the ART, when .class was compiled to .dex (Can be more than one). AOT (Ahead-of-Time) will compile .dex to .oat after that.


        That why a project that required minimum SDK 21 will use ART and say goodbye to Over 65K Methods problem, yeah!

3. Use Proguard to reduce useless method

        This solution is suits for Release build more than Debug build. Because it take a longer time and difficult to log analyze from LogCat.

        Normally, Proguard will shrink and Obfuscate the code when you build a project, so unused method will be removed (But you can keep some method or class by config a proguard-rules.pro file) and also reduce total method count of your project.

4. OK! I give up and use MultiDex

        MultiDex is the last way, if 3 ways that I mentioned aren't suitable for you. MultiDex allows you to build an APK with Dalvik by contain with multiple .dex with over 65,536 methods. Google has publish the MultiDex library to support with pre-Lollipop that use Dalvik to build project to .apk by required Android SDK Build Tools version 21.1 or higher.

Limitation of MultiDex library

        • May occurs ANR while app launching if .dex files too large.
        • Should be define minimum SDK to version 14 or higher.
        • MulitDex uses more memory and may crash while app running if allocation memory is over limit.
        • Take more build time when you build the project.

How to use MultiDex in your project.

        Add MultiDex dependencies in build.gradle your target module like this.

android {

    ...
    buildToolsVersion "21.1.0"

    defaultConfig {

        minSdkVersion 14
        ...

        multiDexEnabled true
    }
    ...
}

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

        Don't forget use build tools 21.1.0 or higher version.

        In Android Manifest, <application> must use MultiDexApplication.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...>
    
    <application
        ...
        android:name="android.support.multidex.MultiDexApplication">

        ...

    </application>
</manifest>

        MultiDexApplication class will manage your project to support with multi .dex files.

        But if you're using custom Application class. Just declare MultiDex method in to your class by follow this.

package com.akexorcist.multidextest;

import android.app.Application;
import android.content.Context;
import android.support.multidex.MultiDex;

public class MyApplication extends Application {

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

        And then use your custom Application class instead of MultiDexApplication.

Use MultiDex while under development.

        Because use MultiDex is take a long time to build a project. So don't use it when your project is under development. It should be use in production release only. But I recommend you to use ART in you under development project by set minimum SDK version to 21 or higher version by use Build Variant to separate build type between develop and production.

...

android {
    ...

    productFlavors {
        develop {
            minSdkVersion 21
        }
        production {
            minSdkVersion 14
            multiDexEnabled true
        }
    }

    ...
}

dependencies {

    ...
    compile 'com.android.support:multidex:1.0.0'

}

How to check total method count in your project?

        I use DexCount ny KeepSafe, the gradle plugin to show method count after finish the gradle building. It's very easy to use it. Just declare like this into buid.gradle of your target module.

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.4.1'
    }
}

apply plugin: 'com.getkeepsafe.dexcount'

       When gradle building has finish, it will be show total method count in console window.


        The report files is the one thing that I like it. Because it show how many method count in the each class in .txt files.


How can we check method count in the each dependencies?

        You can check it by use Methods Count - Your solution for a perfectly fit APK. Just put some dependency package that you want to know.


        If open the website is too lazy for you. They also have the plugin for Android Studio. Just browse for the Android Methods Count plugin and get it.

        This plugin will show the blue circle symbol in the front of each dependencies. And it will show method count information when the mouse pointer hover on it.


Conclusion

        It is common for big project to facing with the Over 65K Methods problem. (yeah, me too) But using MultiDex isn't the best way to solve this problem. It last way if you can fix this problem with another way. Reduce dependencies is the first thing that you have to do.

        And recommend you to use 3rd party plugin to make the method count checking more convenient.




เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium