为什么我在使用 Proguard 时从 Play Services lib 得到 ClassNotFoundException?

Why am I getting a ClassNotFoundException from Play Services lib when using Proguard?

我刚刚在我的构建中打开了 ProGuard,现在我得到了

java.lang.ClassNotFoundException: Didn't find class "com.google.android.gms.chimera.GmsModuleInitializer" on path: DexPathList[[zip file "/system/app/PlayGames.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]

文档说 Android Gradle 插件应该包含我在 Play 服务中使用 Proguard 所需的一切:

Note: ProGuard directives are included in the Play services client libraries to preserve the required classes. The Android Plugin for Gradle automatically appends ProGuard configuration files in an AAR (Android ARchive) package and appends that package to your ProGuard configuration. During project creation, Android Studio automatically creates the ProGuard configuration files and build.gradle properties for ProGuard use. To use ProGuard with Android Studio, you must enable the ProGuard setting in your build.gradle buildTypes. For more information, see the ProGuard guide.

这是我的应用程序模块 build.gradle 文件的重要部分:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"
    defaultConfig {
        ...
    }
    ...
    buildTypes {
        ...{
            applicationIdSuffix ".debug"
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                    'proguard-rules.pro'
        }
    }
}


dependencies {
    ...

    //google play services
    compile 'com.google.android.gms:play-services-gcm:8.4.0'
    compile 'com.google.android.gms:play-services-analytics:8.4.0'
    compile 'com.google.android.gms:play-services-location:8.4.0'
}

这是我的顶级build.gradle文件:

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.2'
    }
}

allprojects {
    repositories {
        jcenter()
        mavenCentral()
    }
}

我错过了什么?

ClassNotFoundException 通常发生在应用程序试图通过其字符串名称加载 class 但找不到具有指定名称的 class 的定义时。

从这个 forum,您可以通过添加 com.google.android.gms.** { *; }.

来修复它

Just add to your proguard-project.txt:

  • keep class com.google.android.gms.** { *; }
  • dontwarn com.google.android.gms.**

您还可以查看此 SO question 中的建议评论。

Google Play Services aars contain proguard.txt with the necessary clauses. So the setting shouldn't really be necessary. You can investigate what happened with the fragment in ProGuard output files. Check app/build/output/mapping/{buildVariant}/usage.txt and mapping.txt. The fragment should be mentioned in one of those.

希望对您有所帮助!

我知道这是一个相当老的 post 但我想强调以下内容,以帮助将来的人。

就像@abielita 提到的那样,ClassNotFoundException 是由未处理的反射案例引起的。

当使用广泛的 -keep 选项(以 .** { *; } 结尾)时,您将指示 ProGuard 不要缩小、优化或混淆所有 classes 和 class 成员通配符之前提到的包名称。这最终将导致优化不佳的项目。因此,最好将 -keep 选项缩小到只针对缺失的 class。在 OP 的示例中,为缺少的 class 添加一个 -keep 选项,如下所示将解决此特定问题;

-keep class com.google.android.gms.chimera.GmsModuleInitializer

如果您将 -addconfigurationdebugging 添加到配置文件,ProGuard 可以帮助您设置缩小的 -keep 选项,ProGuard 手册 here 中记录了有关此功能的更多详细信息。

最近发布了 ProGuard Playground,您可以快速直观地看到广泛的 -keep 选项与缩小的选项的效果。这里的一个好处是您不需要不断地(重新)构建项目。