使用Proguard Obfuscation时,Gson EnumTypeAdapter中的AssertionError

时间:2013-03-21 09:26:00

标签: android gson proguard

我的项目在序列化/反序列化期间在TypeAdapter中实现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)

My 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有类似的问题。

欢迎来自开发者社区的任何帮助。

6 个答案:

答案 0 :(得分:126)

似乎我们必须要求列举&#39;成员被保留。 将它添加到proguard配置文件对我有用:

-keepclassmembers enum * { *; }

或者,如果你想更具体,

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

答案 1 :(得分:17)

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

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

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

答案 2 :(得分:12)

已经建议您需要以保持与序列化对象相关的每个枚举完整的方式配置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相关的示例代码,所以我认为没有必要。

答案 3 :(得分: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

答案 4 :(得分:5)

在我的情况下,proguard被配置为Gson触及的-keep个别类,但是当我配置proguard以保留所在的那些单独的类时,错误就消失了:

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

答案 5 :(得分:0)

请验证以下内容-

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

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

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

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

    -keepclassmembers枚举* {*; }

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