Using proguard with GSON and RoboGuice fails when using a EnumTypeAdapter

匿名 (未验证) 提交于 2019-12-03 08:59:04

问题:

When trying to use a EnumTypeAdapter with Proguard, it results in a AssertionError in Gson.getAdapter(). This error seems to be due to type information being lost... below is all the relevant source code:

The Exception:

03-18 13:27:12.905: ERROR/roboguice(12139): Throwable caught during background processing     java.lang.AssertionError     at com.google.gson.internal.bind.TypeAdapters$EnumTypeAdapter.<init>(Unknown Source)     at com.google.gson.internal.bind.TypeAdapters$24.create(Unknown Source)     at com.google.gson.Gson.getAdapter(Unknown Source) 

The EnumTypeAdapter being used:

public class OperationResultSerializer implements JsonDeserializer<OperationResult>, JsonSerializer<OperationResult> {  @Override public OperationResult deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {     int value = json.getAsJsonPrimitive().getAsInt();     return OperationResult.values()[value]; }  @Override public JsonElement serialize(OperationResult src, Type typeOfSrc, JsonSerializationContext context) {     return new JsonPrimitive(src.ordinal()); } } 

How I am constructing my GSON object:

            gson = new GsonBuilder()                 .registerTypeAdapter(Calendar.class, new WcfCalendarSerializer())                 .registerTypeAdapter(OperationResult.class, new OperationResultSerializer())                 .registerTypeAdapter(FieldName.class, new FieldNameSerializer())                 .registerTypeAdapter(MealType.class, new MealTypeSerializer())                 .create(); 

My ProGuard Config:

#-dontusemixedcaseclassnames: Necessary when building on windows where x.class and X.class is the same file -dontusemixedcaseclassnames  -keepattributes *Annotation* -keepattributes Signature  # Preserve the special static methods that are required in all enumeration classes. -keepclassmembers enum * {     public static **[] values();     public static ** valueOf(java.lang.String); }  -keep public class * extends android.app.Application -keep public class * extends android.app.Activity -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider -keep class * extends android.view.View {    public <init>(android.content.Context);    public <init>(android.content.Context, android.util.AttributeSet);    public <init>(android.content.Context, android.util.AttributeSet, int);    public void set*(...);  } -keep class com.google.inject.** { *; }  -keep class javax.inject.** { *; }  -keep class javax.annotation.** { *; }  -keep class roboguice.** { *; }   -keep class * extends android.preference.Preference {    public <init>(android.content.Context);    public <init>(android.content.Context, android.util.AttributeSet);    public <init>(android.content.Context, android.util.AttributeSet, int);    public void set*(...);  }     # Gson specific classes -keep class sun.misc.Unsafe { *; } #-keep class com.google.gson.stream.** { *; }  ###Action bar sherlock  -keep class android.support.v4.app.** { *; }  -keep interface android.support.v4.app.** { *; }  -keep class com.actionbarsherlock.** { *; }  -keep interface com.actionbarsherlock.** { *; }   ###RoboGuice  -keepclassmembers class * {      @com.google.inject.Inject <init>(...);  }  -keepclassmembers class * {      void *(**On*Event);  }  -keep public class roboguice.**  -keep class com.google.inject.**  -keep class com.google.gson.** {*;}  #datacontract -keep public class com.ordering.datacontract.* -keepclassmembers class com.ordering.datacontract.*  # LVL License binder class -keep class com.android.vending.licensing.ILicensingService       -dontwarn -ignorewarnings 

As stated earlier, I suspect something is failing due to type information being lost - after digging into the GSON source code, this is the code that is called to resolve the EnumTypeAdapter... clearly the getEnumConstants is returning a Name that does not exist as a field of the type classOfT. But I am unsure how that is possible.

private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> { private final Map<String, T> nameToConstant = new HashMap<String, T>(); private final Map<T, String> constantToName = new HashMap<T, String>();  public EnumTypeAdapter(Class<T> classOfT) {   try {     for (T constant : classOfT.getEnumConstants()) {       String name = constant.name();       SerializedName annotation = classOfT.getField(name).getAnnotation(SerializedName.class);       if (annotation != null) {         name = annotation.value();       }       nameToConstant.put(name, constant);       constantToName.put(constant, name);     }   } catch (NoSuchFieldException e) {     throw new AssertionError();   } } public T read(JsonReader in) throws IOException {   if (in.peek() == JsonToken.NULL) {     in.nextNull();     return null;   }   return nameToConstant.get(in.nextString()); }  public void write(JsonWriter out, T value) throws IOException {   out.value(value == null ? null : constantToName.get(value)); }   } 

I have searched all over for possible solutions but have not found much help. Maybe someone has run into this before and can point me in the right direction?

回答1:

I went through and examined the resulting APK decompiled. I believe the problem is related to some enum type losing its members during obfuscation.

Be sure to keep enum class members:

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

Also - make sure ALL of the classes being used in GSON are being retained:

 -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 *;  } 

See full config @ pastebin.com/r5Jg3yY2



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!