使用Proguard Obfuscation时,Gson EnumTypeAdapter中的AssertionError

waq*_*lam 65 android proguard gson

我的项目在序列化/反序列化期间实现了一个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)
Run Code Online (Sandbox Code Playgroud)

我的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  ----------
Run Code Online (Sandbox Code Playgroud)

我正在使用的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我对Gson特有的这个错误做了很多搜索,但找不到任何有用的答案.但是我发现了类似问题的另一个问题.

任何开发人员社区的帮助将不胜感激.

pan*_*dre 151

似乎我们必须要求保留枚举成员.这对我有用:

-keepclassmembers enum * { *; }
Run Code Online (Sandbox Code Playgroud)

或者,如果你想更具体,

-keepclassmembers enum com.your.package.** { *; }
Run Code Online (Sandbox Code Playgroud)

  • 这应该是答案,它引用了确切的异常.谢谢! (4认同)
  • 正确答案,但你能否解释一下原因?谢谢 (2认同)

Eri*_*une 19

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

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

-keepclassmembers class com.example.domain.** {
    <fields>;
}
Run Code Online (Sandbox Code Playgroud)

  • 原样:<fields>,<methods>,<init>,**,*和?是ProGuard认可的通配符. (11认同)
  • 是`<fields>`这里是一个占位符,用于枚举字段的名称,还是应该按原样写入? (9认同)

Lev*_*son 14

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

使用空接口指示类或枚举参与Gson序列化:

public interface GsonSerializable { }

public class MyClass implements GsonSerializable {

    public enum MyEnum implements GsonSerializable {
        enumvalue1, enumvalue2
    }

    public MyEnum mydata1;
}
Run Code Online (Sandbox Code Playgroud)

使用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
Run Code Online (Sandbox Code Playgroud)

就是这样,只要你的类和枚举使用界面,你应该没问题.您还可以在序列化/反序列化方法中强制使用此接口,因此在以后添加新类时不要忘记它:

public String serializeWithGson(GsonSerializable object) { ... }
Run Code Online (Sandbox Code Playgroud)

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


Egg*_*n87 5

在遇到同样的问题之后,我经历了检查生成的反编译APK.我认为这个问题与某些枚举类型在混淆时失去其成员有关.

一定要保持枚举:

 -keepclassmembers enum * {
     public static **[] values();
     public static ** valueOf(java.lang.String);
 }
Run Code Online (Sandbox Code Playgroud)

另外 - 确保保留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 *;
 }
Run Code Online (Sandbox Code Playgroud)

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


Ric*_*mer 5

在我的例子中,proguard配置为-keepGson触及的各个类,但是当我配置proguard以保留这些单独的类所在的时,错误就消失了:

-keep class com.company.library.model.** { *; }
Run Code Online (Sandbox Code Playgroud)