Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the good strategy when dealing with Proguard, MultiDex, Testing and Product Flavors?

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
        }
    }
like image 928
Anthony Avatar asked Apr 14 '16 08:04

Anthony


People also ask

How does ProGuard obfuscation work?

In the obfuscation step, ProGuard renames classes and class members that are not entry points. In this entire process, keeping the entry points ensures that they can still be accessed by their original names. The preverification step is the only step that doesn't have to know the entry points.

What is ProGuard used for?

ProGuard is a tool to help minify, obfuscate, and optimize your code. It is not only especially useful for reducing the overall size of your Android application as well as removing unused classes and methods that contribute towards the intrinsic 64k method limit of Android applications.

What is ProGuard optimization?

ProGuard is an open source command-line tool that shrinks, optimizes and obfuscates Java code. It is able to optimize bytecode as well as detect and remove unused instructions.


1 Answers

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 !

like image 197
Anthony Avatar answered Sep 20 '22 03:09

Anthony