Search code examples
androiddependenciesbuild.gradleandroid-multidex

How to properly reduce an app method count (below dex-limit)


I started a new app which uses firebase features heavily as well as the support libraries. I quickly hit the 65k dex limit, even though there is no reason I should be there given the simplicity of the app. I know I need to exclude certain dependencies that I have no use for, so I have been using gradlew app:dependencies to analyze the dependencies and pull out what is pulled in more than once or things I don't need.

Despite tons of excludes, I'm unable to get the method count down considerably (only 3%). Am I just stuck with multi-dex? If not, how do I effectively decrease method count more?

More information:

  • minSdk 19

Here is the before/after of build.gradle depencies block and the gradlew app:dependencies output. I'm happy to post the dexcount text output of how many methods each library has if that will help. I can say that the biggest contributors to method count are the support libs and com.google.common.* and com.google.android.gms.*

Before Slimming

  • Total methods in app-debug.apk: 67707 (103.31% used)

After Slimming

  • Total methods in app-debug.apk: 65852 (100.48% used)

Before: build.gradle dependency block (no excludes)

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:multidex:1.0.2'
    implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
    implementation "com.android.support:recyclerview-v7:$supportLibraryVersion"
    implementation "com.android.support:design:$supportLibraryVersion"
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation "com.android.support:cardview-v7:$supportLibraryVersion"
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    implementation 'io.reactivex.rxjava2:rxjava:2.1.5'
    implementation('com.oakwoodsc.rxfirestore:rxfirestore-debug:1.0@aar') {
        exclude group: 'io.reactivex.rxjava2', module: 'rxandroid'
        exclude group: 'io.reactivex.rxjava2', module: 'rxjava'
    }
    implementation 'com.firebaseui:firebase-ui-auth:3.1.0' // Remove once custom version
    implementation "com.google.android.gms:play-services-location:$firebaseVersion"
    implementation "com.google.firebase:firebase-firestore:$firebaseVersion"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

Before: gradlew app:dependencies output

Lots of duplication!

+--- com.google.firebase:firebase-core:11.4.2
|    \--- com.google.firebase:firebase-analytics:11.4.2
|         +--- com.google.android.gms:play-services-basement:11.4.2
|         |    +--- com.android.support:support-v4:25.2.0 -> 26.1.0
|         |    |    +--- com.android.support:support-compat:26.1.0
|         |    |    |    +--- com.android.support:support-annotations:26.1.0
|         |    |    |    \--- android.arch.lifecycle:runtime:1.0.0
|         |    |    |         +--- android.arch.lifecycle:common:1.0.0
|         |    |    |         \--- android.arch.core:common:1.0.0
|         |    |    +--- com.android.support:support-media-compat:26.1.0
|         |    |    |    +--- com.android.support:support-annotations:26.1.0
|         |    |    |    \--- com.android.support:support-compat:26.1.0 (*)
|         |    |    +--- com.android.support:support-core-utils:26.1.0
|         |    |    |    +--- com.android.support:support-annotations:26.1.0
|         |    |    |    \--- com.android.support:support-compat:26.1.0 (*)
|         |    |    +--- com.android.support:support-core-ui:26.1.0
|         |    |    |    +--- com.android.support:support-annotations:26.1.0
|         |    |    |    \--- com.android.support:support-compat:26.1.0 (*)
|         |    |    \--- com.android.support:support-fragment:26.1.0
|         |    |         +--- com.android.support:support-compat:26.1.0 (*)
|         |    |         +--- com.android.support:support-core-ui:26.1.0 (*)
|         |    |         \--- com.android.support:support-core-utils:26.1.0 (*)
|         |    \--- com.google.android.gms:play-services-basement-license:11.4.2
|         +--- com.google.firebase:firebase-common:11.4.2
|         |    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|         |    +--- com.google.android.gms:play-services-tasks:11.4.2
|         |    |    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|         |    |    \--- com.google.android.gms:play-services-tasks-license:11.4.2
|         |    \--- com.google.firebase:firebase-common-license:11.4.2
|         +--- com.google.firebase:firebase-analytics-impl:11.4.2
|         |    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|         |    +--- com.google.firebase:firebase-iid:11.4.2
|         |    |    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|         |    |    +--- com.google.firebase:firebase-common:11.4.2 (*)
|         |    |    \--- com.google.firebase:firebase-iid-license:11.4.2
|         |    +--- com.google.firebase:firebase-common:11.4.2 (*)
|         |    +--- com.google.android.gms:play-services-tasks:11.4.2 (*)
|         |    \--- com.google.firebase:firebase-analytics-impl-license:11.4.2
|         \--- com.google.firebase:firebase-analytics-license:11.4.2
+--- com.android.support:multidex:1.0.2
+--- com.android.support:appcompat-v7:26.1.0
|    +--- com.android.support:support-annotations:26.1.0
|    +--- com.android.support:support-v4:26.1.0 (*)
|    +--- com.android.support:support-vector-drawable:26.1.0
|    |    +--- com.android.support:support-annotations:26.1.0
|    |    \--- com.android.support:support-compat:26.1.0 (*)
|    \--- com.android.support:animated-vector-drawable:26.1.0
|         +--- com.android.support:support-vector-drawable:26.1.0 (*)
|         \--- com.android.support:support-core-ui:26.1.0 (*)
+--- com.android.support:recyclerview-v7:26.1.0
|    +--- com.android.support:support-annotations:26.1.0
|    +--- com.android.support:support-compat:26.1.0 (*)
|    \--- com.android.support:support-core-ui:26.1.0 (*)
+--- com.android.support:design:26.1.0
|    +--- com.android.support:support-v4:26.1.0 (*)
|    +--- com.android.support:appcompat-v7:26.1.0 (*)
|    +--- com.android.support:recyclerview-v7:26.1.0 (*)
|    \--- com.android.support:transition:26.1.0
|         +--- com.android.support:support-annotations:26.1.0
|         \--- com.android.support:support-v4:26.1.0 (*)
+--- com.android.support.constraint:constraint-layout:1.0.2 -> 1.1.0-beta1
|    \--- com.android.support.constraint:constraint-layout-solver:1.1.0-beta1
+--- com.android.support:cardview-v7:26.1.0
|    \--- com.android.support:support-annotations:26.1.0
+--- io.reactivex.rxjava2:rxandroid:2.0.1
|    \--- io.reactivex.rxjava2:rxjava:2.0.1 -> 2.1.5
|         \--- org.reactivestreams:reactive-streams:1.0.1
+--- io.reactivex.rxjava2:rxjava:2.1.5 (*)
+--- com.oakwoodsc.rxfirestore:rxfirestore-debug:1.0
+--- com.firebaseui:firebase-ui-auth:3.1.0
|    +--- com.android.support:design:26.1.0 (*)
|    +--- com.android.support:customtabs:26.1.0
|    |    +--- com.android.support:support-compat:26.1.0 (*)
|    |    \--- com.android.support:support-annotations:26.1.0
|    +--- com.android.support.constraint:constraint-layout:1.1.0-beta1 (*)
|    +--- com.google.firebase:firebase-auth:11.4.2
|    |    +--- com.google.android.gms:play-services-base:11.4.2
|    |    |    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|    |    |    +--- com.google.android.gms:play-services-tasks:11.4.2 (*)
|    |    |    \--- com.google.android.gms:play-services-base-license:11.4.2
|    |    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|    |    +--- com.google.firebase:firebase-common:11.4.2 (*)
|    |    +--- com.google.android.gms:play-services-tasks:11.4.2 (*)
|    |    \--- com.google.firebase:firebase-auth-license:11.4.2
|    +--- com.google.android.gms:play-services-auth:11.4.2
|    |    +--- com.google.android.gms:play-services-auth-api-phone:11.4.2
|    |    |    +--- com.google.android.gms:play-services-base:11.4.2 (*)
|    |    |    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|    |    |    +--- com.google.android.gms:play-services-tasks:11.4.2 (*)
|    |    |    \--- com.google.android.gms:play-services-auth-api-phone-license:11.4.2
|    |    +--- com.google.android.gms:play-services-auth-base:11.4.2
|    |    |    +--- com.google.android.gms:play-services-base:11.4.2 (*)
|    |    |    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|    |    |    +--- com.google.android.gms:play-services-tasks:11.4.2 (*)
|    |    |    \--- com.google.android.gms:play-services-auth-base-license:11.4.2
|    |    +--- com.google.android.gms:play-services-base:11.4.2 (*)
|    |    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|    |    +--- com.google.android.gms:play-services-tasks:11.4.2 (*)
|    |    \--- com.google.android.gms:play-services-auth-license:11.4.2
|    \--- com.android.support:cardview-v7:26.1.0 (*)
+--- com.google.android.gms:play-services-location:11.4.2
|    +--- com.google.android.gms:play-services-base:11.4.2 (*)
|    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|    +--- com.google.android.gms:play-services-tasks:11.4.2 (*)
|    \--- com.google.android.gms:play-services-location-license:11.4.2
\--- com.google.firebase:firebase-firestore:11.4.2
     +--- com.google.android.gms:play-services-basement:11.4.2 (*)
     +--- com.google.firebase:firebase-common:11.4.2 (*)
     +--- com.google.android.gms:play-services-tasks:11.4.2 (*)
     +--- com.squareup.okhttp:okhttp:2.7.2
     |    \--- com.squareup.okio:okio:1.6.0
     \--- com.google.guava:guava:20.0

After: build.gradle dependency block (with excludes all over)

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation("com.google.android.gms:play-services-base:$playServicesVersion") {
        exclude group: "com.android.support", module: "support-v4"
    }
    implementation 'com.android.support:multidex:1.0.2'
    implementation "com.android.support:design:$supportLibraryVersion"
    implementation("com.firebaseui:firebase-ui-auth:3.1.0") {  // Remove once custom version
        exclude group: "com.google.android.gms", module: "play-services-base"
        exclude group: "com.google.android.gms", module: "play-services-basement"
        exclude group: "com.google.android.gms", module: "play-services-tasks"
        exclude group: "com.android.support", module: "design"
        exclude group: "com.android.support", module: "support-compat"
        exclude group: "com.android.support", module: "support-annotations"
        exclude group: "com.android.support", module: "cardview-v7"
    }
    implementation("com.android.support:cardview-v7:$supportLibraryVersion") {
        exclude group: "com.android.support", module: "support-annotations"
    }
    implementation('com.oakwoodsc.rxfirestore:rxfirestore-debug:1.0@aar') {
        exclude group: 'io.reactivex.rxjava2', module: 'rxandroid'
        exclude group: 'io.reactivex.rxjava2', module: 'rxjava'
    }
    implementation("com.google.android.gms:play-services-location:$playServicesVersion") {
        exclude group: "com.google.android.gms", module: "play-services-base"
        exclude group: "com.google.android.gms", module: "play-services-tasks"
        exclude group: "com.google.android.gms", module: "play-services-basement"
    }
    implementation("com.google.firebase:firebase-firestore:$playServicesVersion") {
        exclude group: "com.google.android.gms", module: "play-services-basement"
        exclude group: "com.google.android.gms", module: "play-services-tasks"
    }
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    implementation 'io.reactivex.rxjava2:rxjava:2.1.5'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

After: gradlew app:dependencies output:

Less duplication...

+--- com.google.android.gms:play-services-base:11.4.2
|    +--- com.google.android.gms:play-services-basement:11.4.2
|    |    \--- com.google.android.gms:play-services-basement-license:11.4.2
|    +--- com.google.android.gms:play-services-tasks:11.4.2
|    |    +--- com.google.android.gms:play-services-basement:11.4.2 (*)
|    |    \--- com.google.android.gms:play-services-tasks-license:11.4.2
|    \--- com.google.android.gms:play-services-base-license:11.4.2
+--- com.android.support:multidex:1.0.2
+--- com.android.support:design:26.1.0
|    +--- com.android.support:support-v4:26.1.0
|    |    +--- com.android.support:support-compat:26.1.0
|    |    |    +--- com.android.support:support-annotations:26.1.0
|    |    |    \--- android.arch.lifecycle:runtime:1.0.0
|    |    |         +--- android.arch.lifecycle:common:1.0.0
|    |    |         \--- android.arch.core:common:1.0.0
|    |    +--- com.android.support:support-media-compat:26.1.0
|    |    |    +--- com.android.support:support-annotations:26.1.0
|    |    |    \--- com.android.support:support-compat:26.1.0 (*)
|    |    +--- com.android.support:support-core-utils:26.1.0
|    |    |    +--- com.android.support:support-annotations:26.1.0
|    |    |    \--- com.android.support:support-compat:26.1.0 (*)
|    |    +--- com.android.support:support-core-ui:26.1.0
|    |    |    +--- com.android.support:support-annotations:26.1.0
|    |    |    \--- com.android.support:support-compat:26.1.0 (*)
|    |    \--- com.android.support:support-fragment:26.1.0
|    |         +--- com.android.support:support-compat:26.1.0 (*)
|    |         +--- com.android.support:support-core-ui:26.1.0 (*)
|    |         \--- com.android.support:support-core-utils:26.1.0 (*)
|    +--- com.android.support:appcompat-v7:26.1.0
|    |    +--- com.android.support:support-annotations:26.1.0
|    |    +--- com.android.support:support-v4:26.1.0 (*)
|    |    +--- com.android.support:support-vector-drawable:26.1.0
|    |    |    +--- com.android.support:support-annotations:26.1.0
|    |    |    \--- com.android.support:support-compat:26.1.0 (*)
|    |    \--- com.android.support:animated-vector-drawable:26.1.0
|    |         +--- com.android.support:support-vector-drawable:26.1.0 (*)
|    |         \--- com.android.support:support-core-ui:26.1.0 (*)
|    +--- com.android.support:recyclerview-v7:26.1.0
|    |    +--- com.android.support:support-annotations:26.1.0
|    |    +--- com.android.support:support-compat:26.1.0 (*)
|    |    \--- com.android.support:support-core-ui:26.1.0 (*)
|    \--- com.android.support:transition:26.1.0
|         +--- com.android.support:support-annotations:26.1.0
|         \--- com.android.support:support-v4:26.1.0 (*)
+--- com.firebaseui:firebase-ui-auth:3.1.0
|    +--- com.android.support:customtabs:26.1.0
|    +--- com.android.support.constraint:constraint-layout:1.1.0-beta1
|    |    \--- com.android.support.constraint:constraint-layout-solver:1.1.0-beta1
|    +--- com.google.firebase:firebase-auth:11.4.2
|    |    +--- com.google.firebase:firebase-common:11.4.2
|    |    |    \--- com.google.firebase:firebase-common-license:11.4.2
|    |    \--- com.google.firebase:firebase-auth-license:11.4.2
|    \--- com.google.android.gms:play-services-auth:11.4.2
|         +--- com.google.android.gms:play-services-auth-api-phone:11.4.2
|         |    \--- com.google.android.gms:play-services-auth-api-phone-license:11.4.2
|         +--- com.google.android.gms:play-services-auth-base:11.4.2
|         |    \--- com.google.android.gms:play-services-auth-base-license:11.4.2
|         \--- com.google.android.gms:play-services-auth-license:11.4.2
+--- com.android.support:cardview-v7:26.1.0
+--- com.oakwoodsc.rxfirestore:rxfirestore-debug:1.0
+--- com.google.android.gms:play-services-location:11.4.2
|    \--- com.google.android.gms:play-services-location-license:11.4.2
+--- com.google.firebase:firebase-firestore:11.4.2
|    +--- com.google.firebase:firebase-common:11.4.2 (*)
|    +--- com.squareup.okhttp:okhttp:2.7.2
|    |    \--- com.squareup.okio:okio:1.6.0
|    \--- com.google.guava:guava:20.0
+--- io.reactivex.rxjava2:rxandroid:2.0.1
|    \--- io.reactivex.rxjava2:rxjava:2.0.1 -> 2.1.5
|         \--- org.reactivestreams:reactive-streams:1.0.1
\--- io.reactivex.rxjava2:rxjava:2.1.5 (*)

Solution

  • First things first, you don't need all those excludes. If two dependencies use com.android.support:support-v4:26.1.0, it is only included once. It is listed twice so you can see who depends on it.

    One option you may want to consider is turning on ProGuard. This will strip out any unused code, including library code. In your case, it will likely get you under the 65k limit.

    To use it, set this in your build.gradle: release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' }

    You will want to create proguard-rules.pro and populate it with some directive specific to your app. Be sure to check out the full documentation to see what you might need to include there.

    While it is possible to stay under the 65k limit without ProGuard, most apps of moderate complexity will surpass it fairly quickly, and I've found that avoiding libraries such as AppCompat in an effort to stay under the 65k limit is generally more work than it is worth.

    Even with ProGuard, some apps will encounter the 65k limit. Other apps may not want to use ProGuard for one reason or another. In these cases, your best option is to enable multidex, which allows you to go over the 65k limit.