使用Proguard模糊处理时,Gson EnumTypeAdapter中出现AssertionError


73

我的项目在序列化/反序列化期间实现了一个TypeAdapterin Gson,以保留对象的多态状态。无论如何,该项目在开发测试期间运行良好,但是当通过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的错误进行了大量搜索,但找不到任何有用的答案。但是,我发现了另一个类似问题。

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


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

Answers:


186

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

-keepclassmembers enum * { *; }

或者,如果您想更具体一点,

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

4
这应该是答案,它指的是确切的异常。谢谢!
丹尼尔·威尔逊

2
正确的答案,但您能否解释原因?谢谢
Xiaogegexiao

原因是Gson的用于枚举的内部适配器在枚举常量上进行迭代(当Proguard重命名时,这也会引起另一个问题Enum.values()),name()然后使用它们来查找字段并进行检查@SerializedName。但是,Proguard重命名了这些字段,因此Gson找不到它们。-keepclassmembers enum防止Proguard重命名字段。(请注意,有一个要求Gson解决的请求
Marcono1234年

19

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

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

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

9
<fields>这里枚举字段的名称的占位符,还是应该写成,是什么?
卡洛斯P

11
照原样:<fields>,<methods>,<init>,**,*和?是ProGuard可以识别的通配符。
Eric Lafortune 2014年

1
我希望Gson会使用一种不太放肆的方法来做同样的事情。即,迭代字段并获取名称,而不是假设名称在运行时与字段名称匹配。
Trejkaz

14

已经建议您以某种方式配置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。** {*; }'指的是一些与Google相关的示例代码,因此我认为没有必要。


7

在我的案例中,将proguard配置为-keepGson接触的单个类,但是当我配置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


0

请验证以下内容-

  1. 在应用程序目录中添加了添加的proguard-rules.pro文件。

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

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

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

    -keepclassmembers枚举* {*; }

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.