【问题标题】:AssertionError in Gson EnumTypeAdapter when using Proguard Obfuscation使用 Proguard 混淆时 Gson EnumTypeAdapter 中的 AssertionError
【发布时间】:2013-03-10 17:33:24
【问题描述】:

我的项目在序列化/反序列化期间在Gson 中实现TypeAdapter,以保留对象的多态状态。无论如何,该项目在开发测试期间运行良好,但当它使用 proguard 混淆 发布并进行测试时,它就崩溃了。

03-21 10:06:53.632: E/AndroidRuntime(12441): FATAL EXCEPTION: main
03-21 10:06:53.632: E/AndroidRuntime(12441): java.lang.AssertionError
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.TypeAdapters$EnumTypeAdapter.<init>(SourceFile:724)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.TypeAdapters$26.create(SourceFile:753)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson.getAdapter(SourceFile:353)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(SourceFile:82)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(SourceFile:81)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(SourceFile:118)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(SourceFile:72)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson.getAdapter(SourceFile:353)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson.toJson(SourceFile:578)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson.toJsonTree(SourceFile:479)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson.toJsonTree(SourceFile:458)
03-21 10:06:53.632: E/AndroidRuntime(12441):    at com.google.gson.Gson$3.serialize(SourceFile:137)

我的 Gson 特定的 proguard 配置是:

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }

#This is extra - added by me to exclude gson obfuscation
-keep class com.google.gson.** { *; }

##---------------End: proguard configuration for Gson  ----------

我正在使用的 TypeAdapter 是:

public final class GsonWorkshiftAdapter implements JsonSerializer<IWorkshift>, JsonDeserializer<IWorkshift> {
    private static final String CLASSNAME = "CLASSNAME";
    private static final String INSTANCE  = "INSTANCE";

    @Override
    public JsonElement serialize(IWorkshift src, Type typeOfSrc, JsonSerializationContext context) {
        String className = src.getClass().getCanonicalName();
        JsonElement elem = context.serialize(src);

        JsonObject retValue = new JsonObject();
        retValue.addProperty(CLASSNAME, className);
        retValue.add(INSTANCE, elem);

        return retValue;
    }

    @Override
    public IWorkshift deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonObject jsonObject =  json.getAsJsonObject();
        JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
        String className = prim.getAsString();

        Class<?> klass = null;
        try { klass = Class.forName(className); }
        catch (ClassNotFoundException e) { throw new JsonParseException(e.getMessage()); }

        return context.deserialize(jsonObject.get(INSTANCE), klass);
    }
}

我针对 Gson 特有的这个错误进行了大量搜索,但找不到任何有用的答案。但是我发现 another question 有类似的问题。

我们将不胜感激开发者社区的任何帮助。

【问题讨论】:

  • 我希望我可以为这个问题投票 100 次。我终于能够解决我的应用程序在生产中崩溃的问题,同时了解更多关于枚举和 proguard 的知识。很好的问题,并感谢所有发布非常好的详细答案的人。 @Eric Lafortune

标签: android gson proguard


【解决方案1】:

似乎我们必须要求保留枚举的成员。 将此添加到 proguard 配置文件对我有用:

-keepclassmembers enum * { *; }

或者,如果你想更具体,

-keepclassmembers enum com.your.package.** { *; }

【讨论】:

  • 这应该是答案,它引用了确切的异常。谢谢!
  • 正确答案,但您能解释一下原因吗?谢谢
  • 原因是 Gson 的枚举内部适配器 iterates over the enum constants (当 Proguard 重命名 Enum.values() 时,这也可能导致不同的问题)并使用他们的 name() 然后找到该字段并检查 @ 987654327@。但是,Proguard 重命名了这些字段,因此 Gson 无法找到它们。 -keepclassmembers enum 阻止 Proguard 重命名字段。 (请注意,Gson 有一个 pull request 作为解决方法)
【解决方案2】:

当 GSON 无法从 JSON 数据反序列化枚举常量时,GSON 会抛出此 AssertionError,对枚举类的字段执行自省。不幸的是,它吞噬了底层 NoSuchFieldException 的细节。

您应该确保保留已序列化的枚举字段(以及一般字段)的名称。默认情况下,ProGuard 可能会重命名甚至删除它们。例如,使用一些通配符:

-keepclassmembers class com.example.domain.** {
    <fields>;
}

【讨论】:

  • &lt;fields&gt;这里是枚举字段名称的占位符,还是应该按原样写?
  • 原样:、**、* 和 ?是 ProGuard 识别的通配符。
  • 我希望 Gson 能够使用一种不那么自以为是的方式来做同样的事情。即,迭代字段并获取名称,而不是假设名称在运行时与字段名称匹配。
【解决方案3】:

已经建议您配置 Proguard 以保持与序列化对象相关的每个枚举的完整性。我不太喜欢必须明确列出所有枚举的事实,这种解决方案很难维护。我想出的一个稍微好一点的解决方案如下。

使用空接口表示某个类或枚举参与了 Gson 序列化:

public interface GsonSerializable { }

public class MyClass implements GsonSerializable {

    public enum MyEnum implements GsonSerializable {
        enumvalue1, enumvalue2
    }

    public MyEnum mydata1;
}

使用 Proguard 配置来保留接口和实现它的所有类/枚举:

# keep GsonSerializable interface, it would be thrown away by proguard since it is empty
-keep class com.example.GsonSerializable

# member fields of serialized classes, including enums that implement this interface
-keepclassmembers class * implements com.example.GsonSerializable {
    <fields>;
}

# also keep names of these classes. not required, but just in case.
-keepnames class * implements com.example.GsonSerializable

就是这样,只要你的类和枚举使用接口,你应该没问题。你也可以在你的序列化/反序列化方法中强制这个接口的存在,所以以后添加新类时不要忘记它:

public String serializeWithGson(GsonSerializable object) { ... }

同样在您的配置中使用 'com.google.gson.examples.android.model.** { *; }' 指的是一些谷歌相关的示例代码,所以我认为没有必要。

【讨论】:

    【解决方案4】:

    在我的例子中,proguard 被配置为 -keep Gson 接触的单个类,但是当我将 proguard 配置为保留这些单个类所在的 时,错误消失了:

    -keep class com.company.library.model.** { *; }
    

    【讨论】:

      【解决方案5】:

      在遇到同样的问题后,我仔细检查了反编译后的 APK。我认为这个问题与某些枚举类型在混淆过程中丢失其成员有关。

      一定要保留枚举:

       -keepclassmembers enum * {
           public static **[] values();
           public static ** valueOf(java.lang.String);
       }
      

      另外 - 确保 GSON 中使用的所有类都被保留:

       -keep public class com.company.ordering.datacontract.** {
           public protected *;
       }
      
       -keep public class com.company.ordering.service.request.** {
           public protected *;
       }
       -keep public class com.company.ordering.service.response.** {
           public protected *;
       }
      

      查看完整配置@pastebin.com/r5Jg3yY2

      【讨论】:

        【解决方案6】:

        请确认以下事项-

        1. 在app目录下添加proguard-rules.pro文件。

        2. 是 build.gradle(module:app) 文件中定义的路径,路径定义正确,如下 -

          proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

        3. 如果以上两个步骤都可以,请在 progaurd 文件中添加以下行(规则)-

          -keepclassmembers 枚举 * { *; }

        4. 清理,构建项目并再次运行。

        【讨论】:

          【解决方案7】:

          在枚举类上应用 androidx.annotation.Keep 注释。喜欢:

          @Keep
          enum class PlayerType {
              PRO,
              INTERMEDIATE,
              BASIC
          }
          

          【讨论】:

            猜你喜欢
            • 2013-10-07
            • 2023-04-03
            • 2013-03-07
            • 2015-12-27
            • 2017-10-24
            • 2012-06-24
            • 2021-08-10
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多