在处理 Proguard、MultiDex、Testing 和 Product Flavors 时有什么好的策略?

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

我有一个应用程序引用了大约 10 万个方法,最小 Sdk = 16

这里有2个组装选项:

现在我有一些常见的用例:

  1. 运行 并在模拟器和设备上进行调试
    • 要求越快越好
  2. 做测试(集成和UI)
    • 它需要 运行(我在使用 MultiDex 运行ing Espresso 时遇到一些问题)
  3. 制作产品 APK
    • 要求可靠,尽量缩小

大家有什么关于拼装攻略的推荐吗?

3/产品

2/测试

1/调试

这是 Gradle 文件:

    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
        }
    }

为了避免 multidex,我在调试和发布版本上都使用了混淆器。

我的 build.gradle 文件看起来像这样:

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

为了最小化调试和发布之间的差异,并允许正确调试调试版本,proguard_debug.pro 文件包含以下混淆指令:

-include proguard_release.pro

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

那样的话,我只维护一个混淆器配置(在 proguard_release.pro 中),调试版本是使用相同的配置构建的,但没有混淆代码。

该配置解决了所有提到的问题:

  1. 不需要 multidex(所以没有两难选择是否与 API 一起使用 21 岁以上,您可以使用 Espresso)
  2. 调试版本和发布版本相同,只是调试版本不会混淆您的代码

根据@Muzikant的提议,我大体赞同,总结了今天的愿景

  • 如果可以,尽量不要使用 MultiDex
    • 测试库带来的方法开销可能会达到 65K 个数(所以使用 MutliDex)
    • MultiDex 可能比 Proguard 进程更快(有待检查),因此调试可能很有趣
  • 尝试使用最接近发布 APK 的 APK 进行测试

我的建议是:

  1. 因为有 3 个构建案例,只需制作 3 个 buildTypes :

    • 发布
    • 调试
    • 验证(test为保留字)
  2. 使用 2 种口味:

    • 一个与您的应用 minSdkVersion 一起发布
    • 还有一个用于开发,使用更新的 minSdkVersion(更快的构建、更多的测试功能、更易于使用的 espresso...)
  3. 不混淆调试

  4. 为了在测试阶段使用 Proguard,需要 Gradle DSL 的特定关键字 testProguardFile('proguard-rules-test.pro')

  5. 指向将用于调试的构建 testBuildType = "validation"

  6. 混淆测试(至少对 UI 系统和 CI 系统上的功能测试)

  7. 仅针对发布版 getDefaultProguardFile('proguard-android-optimize.txt') 使用优化 Proguard 规则,对于测试和调试只需使用 getDefaultProguardFile('proguard-android.txt')

我的 Proguard 文件的架构如下:

  1. release proguard-rules-release.pro 的一个主文件,其中包含一组专用 Proguard 文件 -include proguard-rules-fabric.pro

  2. 用于 debug proguard-rules-debug.pro 的第二个文件,其中包括 proguard-rules-release.pro

  3. 第三个文件用于 调试 proguard-rules-dontobfuscate.pro 禁用混淆

  4. 测试proguard-rules-test.pro的第四个文件,其中包括proguard-rules-debug.pro和测试所需的规则

这是 Gradle 文件:

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')
        }
    }
...
}

我还有一些未解决的问题需要解决:

  • 如何使用 Android Studio 的自动调试签名进行验证构建? (但我不确定影响)

  • 我仍然需要在验证 BuildType 中添加 proguardFiles 属性,而我有包含调试的 testProguardFile('proguard-rules-test.pro')