6

I have an app that reference ~ 100K methods, with min Sdk = 16

Here is 2 option for assembling:

  • Proguard shrink this bunch of methods to only 44K methods
  • Use Multi Dex

Now I have some common use cases:

  1. Run and debug on emulator and devices
    • It requires to be as fast as possible
  2. Do tests (Integration and UI)
    • It requires to run (I have some issue running Espresso with MultiDex)
  3. Make the Prod APK
    • It requires to be reliable and shrinked as possible

Do you have guys some recommandation about the assembling strategy ?

3/ Prod

  • Use Proguard to reduce APK size
  • Use Proguard to obfuscate
  • Do not use Multidex as most as possible (it may failed)

2/ Test

  • Use minSdkVersion 21 (I read that starting by 21 enable pre-dexing, that saves time)
  • ???

1/ Debug

  • Use minSdkVersion 21 (I read that starting by 21 enable pre-dexing, that saves time)
  • ???

Here is the Gradle file :

    productFlavors {
        dev {
            minSdkVersion 21
            multiDexEnabled ???
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        prod {
            // The actual minSdkVersion for the application.
            minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION
            multiDexEnabled false
        }
    }
    defaultConfig {
        applicationId "xxxx"
        targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION
        minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION
        versionCode ANDROID_BUILD_VERSION_CODE
        versionName ANDROID_BUILD_APP_VERSION_NAME
    }

    buildTypes {
        release {
            debuggable false
            ext.enableCrashlytics = true
            renderscriptOptimLevel 3
            signingConfig android.signingConfigs.release
            zipAlignEnabled true
            minifyEnabled true
            //  shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        debug {
            debuggable true
            renderscriptOptimLevel 3
            applicationIdSuffix ".debug"
            versionNameSuffix "debug"
            minifyEnabled false
        }
    }
Anthony
  • 3,989
  • 2
  • 30
  • 52

2 Answers2

2

Based on the proposal of @Muzikant, that I mainly agree, I summarized my today vision

  • Try not using MultiDex if you can.
    • It may happen to reach the 65K number with the overhead of method brings with test libraries (so use MutliDex)
    • It may happen that MultiDex is faster than Proguard process (to be checked), so it could be interesting for debugging
  • Try to use an APK for testing that is the closest to the release APK

My recommendations are:

  1. as there are 3 builds cases, just make 3 buildTypes :

    • release
    • debug
    • validation (test is a reserved word)
  2. use 2 flavors:

    • one for release with the minSdkVersion of your app
    • and one for development that uses a more up-to-date minSdkVersion (faster building, more features for testing, easier to use espresso...)
  3. do not obfuscate for debugging

  4. for using Proguard during the test phase, a specific keyword of the Gradle DSL is necessary testProguardFile('proguard-rules-test.pro')

  5. point the build that will be used for debugging with testBuildType = "validation"

  6. do obfuscate for testing (at least for UI system and functional tests on your CI system)

  7. use the optimisation Proguard rules only for release getDefaultProguardFile('proguard-android-optimize.txt'), for testing and debug just use getDefaultProguardFile('proguard-android.txt')

The architecture of my Proguard files is the following:

  1. one main file for release proguard-rules-release.pro that include a set of dedicated Proguard files with -include proguard-rules-fabric.pro

  2. one second file for debug proguard-rules-debug.pro that include proguard-rules-release.pro

  3. one third file for debug proguard-rules-dontobfuscate.pro that disable obfuscation

  4. one forth file for testing proguard-rules-test.pro that include proguard-rules-debug.pro and the rules necessary for testing

Here is the Gradle file:

android {
    ...
    compileSdkVersion ANDROID_BUILD_SDK_VERSION
    buildToolsVersion ANDROID_BUILD_TOOLS_VERSION

    productFlavors {
        // Define separate dev and prod product flavors.
        dev {
            // dev utilizes minSDKVersion = 21 to allow the Android gradle plugin
            // to pre-dex each module and produce an APK that can be tested on
            // Android Lollipop without time consuming dex merging processes.
            minSdkVersion 21
            multiDexEnabled false

        }
        prod {
            // The actual minSdkVersion for the application.
            minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION
            multiDexEnabled false
        }
    }
    defaultConfig {
        applicationId "x.y.app.z"
        targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION
        minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION
        versionCode ANDROID_BUILD_VERSION_CODE
        versionName ANDROID_BUILD_APP_VERSION_NAME
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    // point thge build for testing
    testBuildType = "validation"

    buildTypes {
        release {
            debuggable false
            ext.enableCrashlytics = true
            renderscriptOptimLevel 3
            signingConfig android.signingConfigs.release
            zipAlignEnabled true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules-release.pro'
        }
        debug {
            debuggable true
            renderscriptOptimLevel 3
            applicationIdSuffix ".debug"
            versionNameSuffix "debug"
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-debug.pro', `proguard-rules-dontobfuscate.pro`
        }

        validation.initWith(debug)
        validation {
            signingConfig android.signingConfigs.release
            debuggable false
            renderscriptOptimLevel 3
            applicationIdSuffix ".test"
            versionNameSuffix "test"
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-debug.pro'
            testProguardFile('proguard-rules-test.pro')
        }
    }
...
}

I still have some open points to solve :

  • How to use the automatic debug signing of Android Studio for the validation build ? (but I'm not sure about the impact)

  • I still have to add proguardFiles attribute in the validation BuildType while I have the testProguardFile('proguard-rules-test.pro') that include the debug !

Anthony
  • 3,989
  • 2
  • 30
  • 52
  • maybe adding validation.initWith(buildTypes.debug) for the first open point (to be checked) – Anthony Apr 25 '16 at 10:05
  • I see the point "use the optimisation Proguard rules only for release getDefaultProguardFile('proguard-android-optimize.txt'), for testing and debug just use getDefaultProguardFile('proguard-android.txt')" is given. Why is that we do not run tests with Optimized code ? I have done tests on my code with obfuscation, but I am facing problems with optimized code. – Dinesh Aug 03 '17 at 14:55
1

I use proguard on both debug and release versions in order to avoid multidex.

my build.gradle file looks like that:

debug {
    minifyEnabled true
    proguardFiles 'proguard_debug.pro'
    signingConfig signingConfigs.debug
    debuggable true
}
release {
    minifyEnabled true
    proguardFiles 'proguard_release.pro'
    signingConfig signingConfigs.release
    debuggable false
}

in order to minimze differences between debug and release, and to allow proper debugging of the debug build, the proguard_debug.pro file contains the following proguard instructions:

-include proguard_release.pro

-dontobfuscate
-dontoptimize
-keep class my.package.name.** {*; }

That way, I only maintain a single proguard configuration (in proguard_release.pro) and the debug version is built using the same configuration but without obfuscating the code.

That configuration solves all mentioned issues:

  1. No multidex is required (so no dilemma whether to use it with API 21+, and you can use Espresso)
  2. Debug and release builds are the same, except that debug build does not obfuscate your code
Muzikant
  • 8,070
  • 5
  • 54
  • 88
  • OK, good. Just note it may also have issues caused by obfuscation ! However building the apk is really slow for debugging & testing. Do you accommodate yourself with this delay? – Anthony Apr 21 '16 at 09:48
  • Considering the alternatives, this is the best solution for me. Obfuscation is not an issue since the debug proguard config has a -dontobfuscate flag – Muzikant Apr 21 '16 at 10:19
  • Yes, I just says that obfuscation may bring bugs into the app. This is also good to test with – Anthony Apr 22 '16 at 12:17
  • Is it possible to run instrumentation test cases with object injection when the code is obfuscated and optimized? – Dinesh Aug 04 '17 at 07:40