在使用EnumTypeAdapter时,对GSON和RoboGuice使用proguard会失败 [英] Using proguard with GSON and RoboGuice fails when using a EnumTypeAdapter

查看:145
本文介绍了在使用EnumTypeAdapter时,对GSON和RoboGuice使用proguard会失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当试图在Proguard中使用EnumTypeAdapter时,它会在Gson.getAdapter()中产生一个AssertionError。这个错误似乎是由于类型信息丢失...下面是所有相关的源代码:



例外:

  03-18 13:27:12.905:错误/ roboguice(12139):在后台处理过程中捕获到
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)

正在使用的EnumTypeAdapter:

  public class OperationResultSerializer实现了JsonDeserializer< OperationResult> ;, JsonSerializer< OperationResult> {

@Override
public OperationResult deserialize(JsonElement json,Type typeOfT,JsonDeserializationContext context)throws JsonParseException {
int value = json.getAsJsonPrimitive()。getAsInt();
返回OperationResult.values()[value];

$ b @Override
public JsonElement serialize(OperationResult src,Type typeOfSrc,JsonSerializationContext context){
return new JsonPrimitive(src.ordinal());


我如何构建我的GSON对象:

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

我的ProGuard配置:

 # -  dontusemixedcaseclassnames:在x.class和X.class是相同文件的窗口上构建时必需的
-dontusemixedcaseclassnames

-keepattributes *注释*
-keepattributes签名

#保留所有枚举类所需的特殊静态方法。
-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特定类
-keep class sun.misc.Unsafe {*; }
#-keep class com.google.gson.stream。** {*; }

###操作栏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许可证绑定程序类
-keep class com.android.vending.licensing.ILicensingService


-dontwarn
--ignorewarnings

$ b $ p如前所述,由于类型信息丢失,我怀疑有些东西是失败的 - 在深入研究GSON源代码之后,这是调用的代码解析EnumTypeAdapter ...显然,getEnumConstants返回一个不存在的名称作为类型classOfT的字段。但我不确定这是可能的。

  private static final class EnumTypeAdapter< T extends Enum< T>>扩展TypeAdapter< T> {
private final Map< String,T> nameToConstant = new HashMap< String,T>();
private final Map< T,String> constantToName = new HashMap< T,String>(); (T常量:classOfT.getEnumConstants()){
字符串名称=常量名称($)

public EnumTypeAdapter(Class< T> classOfT){
try {
();
SerializedName annotation = classOfT.getField(name).getAnnotation(SerializedName.class);
if(annotation!= null){
name = annotation.value();
}
nameToConstant.put(name,constant);
constantToName.put(常量,名称);
}
} catch(NoSuchFieldException e){
throw new AssertionError();

$ b $ public T read(JsonReader in)throws IOException {
if(in.peek()== JsonToken.NULL){
in.nextNull );
返回null;
}
return nameToConstant.get(in.nextString());

$ b $ public void write(JsonWriter out,T value)throws IOException {
out.value(value == null?null:constantToName.get(value));
}
}

我已经搜索了所有可能的解决方案,但没有找到了很多帮助。也许有人遇到过这种情况,可以指引我走向正确的方向吗?

解决方案

我检查了反编译的结果APK。我相信这个问题与某些枚举类型在模糊处理期间失去其成员有关。



请确保保留enum类成员:

  -keepclassmembers枚举* {
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


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?

解决方案

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

这篇关于在使用EnumTypeAdapter时,对GSON和RoboGuice使用proguard会失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆