2013-03-21 33 views
35

私のプロジェクトは、オブジェクトの多態性状態を保持するために、シリアル化/逆シリアル化中ににTypeAdapterを実装しています。いずれにせよ、このプロジェクトは開発テスト中に正常に動作しますが、proguard難読化でリリースされ、テストされたときにクラッシュします。Proguard難読化を使用する場合のGson EnumTypeAdapterのAssertionError

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に固有のこのエラーの検索をたくさんやりました役に立った回答は見つかりませんでした。しかし、私は同様の問題でanother questionを見つけました。

開発者のコ​​ミュニティからの助けに感謝します。

+0

を私はこの質問を100回upvoteことがしたいです。私は最終的に、プロダクションでのアプリのクラッシュを解決することができました。同時にenumとproguardについてもう少し学びました。大きな質問と、非常に良い答えを投稿した皆様に感謝します。 @Eric Lafortune – Brandon

答えて

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

を参照してください。
16

GSONは、JSONデータから列挙定数を直列化解除できない場合にこのAssertionErrorをスローし、列挙型クラスのフィールドでイントロスペクションを実行します。残念ながら、それは根底にあるNoSuchFieldExceptionの詳細を呑み込んでいます。

シリアル化されている列挙型フィールド(および一般的なフィールド)の名前を保持する必要があります。デフォルトでは、ProGuardは名前を変更したり削除したりすることがあります。例えば、いくつかのワイルドカードで:

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

は ' 'です。ここでは、列挙型フィールドの名前のプレースホルダ、またはそのまま記述する必要がありますか? –

+10

そのまま:,,、**、*、および? ProGuardが認識するワイルドカードです。 –

+0

私はGsonが同じことをするためにあまり予期せぬ方法を使用したかったでしょう。名前が実行時にフィールド名と一致すると仮定するのではなく、フィールドを反復して名前を取得する。 – Trejkaz

9

それはすでにあなたがそれをそのままシリアライズされたオブジェクトに関連するすべての列挙型を保つような方法で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 

これは、クラスと列挙型がインターフェイスを使用している限り、あなたはOKであるはずです。新しいクラスを追加するときに、後でそれを忘れないようにあなたも、あなたのシリアライズ/デシリアライズする方法で、このインタフェースの存在を強制することができます:

public String serializeWithGson(GsonSerializable object) { ... } 

また、あなたの設定で「com.google.gsonの行を.examples.android.model。** {*; } 'はGoogle関連のサンプルコードの一部を指しているので、必要ではないと私は思います。私の場合は

4

は、ProGuardのはGsonに触れ-keep個々のクラスに設定されていたが、私はそれらの個々のクラスが住んパッケージ保持するProGuardのを設定したときにエラーが去っていきました:

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

それはのように思えるが列挙のメンバーが保持されるように要求しなければなりません。 これが私の仕事:

-keepclassmembers enum * { *; } 

それとも、あなたはより具体的にしたい場合は、

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

これは答えになるはずです。正確な例外を参照しています。ありがとうございました! –

+1

親指で(y)、あなたは私の問題を解決しました。 –

+0

正解ですが、その理由を親切に説明できますか?ありがとう – Xiaogegexiao

関連する問題