在Android上使用ProGuard和Gson(类转换异常)

34
我在使用Gson和ProGuard时遇到了很大的问题。我有一个简单的对象,当我将其解析为json,保存到sqllite并从数据库中读取以将json加载回我的对象时,我会得到一个java.lang.classcast异常。如果我不使用ProGuard,一切都正常运行。 我已经验证了发送到和从数据库中获取的json字符串是相同的。异常并非在转换json时抛出,而是在尝试访问对象时抛出。 这是我的简单对象:
public class ScanLog extends ArrayList<SingleFrame>
{
     private static final long serialVersionUID = 1L;

     public ScanLog()
     {

     }
}

public final class SingleFrame 
{
    public int Position;
    public int Time;
    public Map<Integer,String> MainDataMap;
    public Map<Integer,String> DataMap;

    public SingleFrame(int position, int time, 
                    Map<Integer,String> mainDataMap, Map<Integer,String> dataMap)
    {
        this.Position = position;
        this.Time = time;
        this.MainDataMap = mainDataMap;
        this.DataMap = dataMap;
    }

}

我的应用程序的其他方面都很好,但是与ProGuard有关的某些问题导致了这种情况......我尝试了所有种类的 -keep 命令在 proguard.cfg 中,但我不确定我所做的是否正确。

编辑 - 添加 proguard.cfg

-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-dontshrink
-dontoptimize

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService

#keep all classes that might be used in XML layouts
-keep public class * extends android.view.View
-keep public class * extends android.app.Fragment
-keep public class * extends android.support.v4.Fragment

#keep all classes
-keep public class *{
public protected *;
}

#keep all public and protected methods that could be used by java reflection
-keepclassmembernames class * {
    public protected <methods>;
}


-keepclasseswithmembernames class * {
    native <methods>;
}

-keep public class org.scanner.scanlog.SingleFrame


-keepclassmembers class org.scanner.scanlog.ScanLog { 
        private <fields>; 
        public <fields>; 
}

-keepclassmembers class org.scanner.scanlog.SingleFrame { 
        private <fields>; 
        public <fields>; 
}

-keepclasseswithmembernames class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembernames class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

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

-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

-dontwarn **CompatHoneycomb
-dontwarn org.htmlcleaner.*
#-keep class android.support.v4.** { *; }

编辑 - 好的,我已经成功地在我的应用程序中设置了ACRA,这是一个非常棒的功能!以下是堆栈跟踪:

java.lang.ClassCastException: java.lang.Object
    at org.scanner.activity.ReaderMainActivity.AdvanceScanLog(SourceFile:1499)
    at org.scanner.activity.r.onProgressChanged(SourceFile:271)
    at android.widget.SeekBar.onProgressRefresh(SeekBar.java:89)
    at android.widget.ProgressBar.doRefreshProgress(ProgressBar.java:507)
    at android.widget.ProgressBar.refreshProgress(ProgressBar.java:516)
    at android.widget.ProgressBar.setProgress(ProgressBar.java:565)
    at android.widget.AbsSeekBar.trackTouchEvent(AbsSeekBar.java:337)
    at android.widget.AbsSeekBar.onTouchEvent(AbsSeekBar.java:292)
    at android.view.View.dispatchTouchEvent(View.java:3932)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:906)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:906)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:906)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:906)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:906)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:906)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:906)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:906)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:906)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1784)
    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1157)
    at android.app.Activity.dispatchTouchEvent(Activity.java:2181)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1759)
    at android.view.ViewRoot.deliverPointerEvent(ViewRoot.java:2336)
    at android.view.ViewRoot.handleMessage(ViewRoot.java:1976)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:143)
    at android.app.ActivityThread.main(ActivityThread.java:4263)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
    at dalvik.system.NativeStart.main(Native Method)

确切的错误信息是什么?你在proguard.cfg文件中放了什么内容? - THelper
我把它加入了。由于我无法调试已构建的应用程序,因此在所有相关的catch块中添加了toast消息。当我尝试访问从json重新创建的对象时,toast消息就出现在那里,报错为:java.lang.ClassCastException: java.lang.object。 - Jesse
1
你可以使用这个 proguard配置。 - Macarse
8个回答

70

但是我使用ProGuard导出apk时遇到了以下异常:Caused by: java.lang.AbstractMethodError: abstract method "void com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$a.a(com.google.gson.stream.JsonWriter, java.lang.Object)" at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:200) at com.google.gson.Gson.toJson(Gson.java:546) at com.google.gson.Gson.toJson(Gson.java:525) at com.google.gson.Gson.toJson(Gson.java:480) at com.google.gson.Gson.toJson(Gson.java:460) - Shajeel Afzal

26

这些配置在我的一个应用中起作用:

# Add the gson class
-keep public class com.google.gson

# Add any classes the interact with gson
-keep class com.someapp.android.models.ChatModel { *; }
-keep class com.someapp.android.models.FeedModel { *; }

# Add the path to the jar
-libraryjars /Users/someuser/Documents/workspace/someapp/lib/gson-1.7.1.jar

希望这能对你有所帮助。


4
这对我有用,但我必须添加:-keepattributes Signature - blindstuff
4
"请添加与gson交互的任何类",这对我很有帮助,谢谢! - Trinimon
@jerry # 添加任何与gson交互的类,这对我很有效,但是当我在apk上应用反向工程时,我得到了所有模型类,成员没有改变,我能够反编译所有模型,这破坏了安全目的,难道没有其他选择吗? - umesh
@blindstuff,您不应该添加-libraryjars。在这份文档中,http://proguard.sourceforge.net/index.html#manual/troubleshooting.html,它说:“您永远不应该明确指定输入jar文件(使用-injars或-libraryjars),因为这样会导致重复定义。” - Matt Accola
4
若有人想要将 Models 包中的所有类都保留,则应编写:-keep class com.someapp.android.models.** { *; } - Shajeel Afzal

14

将在Gson项目中找到的更改应用在我的Android示例中对我有效。

需要添加的行为:

-keepattributes Signature
-keep class sun.misc.Unsafe { *; }
# and keeping the classes that will be serialized/deserialized

你提到的那几行对我来说不够,但是我应用了你提供的例子中的内容,对我很有帮助。谢谢。 - Murat

6

我知道原问题已经通过采用不同的方法解决了,但我在使用flexjson和Android上的Proguard时遇到了非常类似的问题,并且我已经解决了它,以防其他人遇到相同的问题。

当将JSON转换回包括一些ArrayLists的值对象时,我会得到相同的ClassCastException。 我通过启用混淆但关闭混淆的所有部分(-keep everything,-keepclassmembers everything和-keepattributes everything)并逐步启用这些部分来使其起作用。

结果是保留整个flexjson库:

-keep class flexjson**
--keepclassmembers class flexjson** {
   *;
}

保留签名和注释属性:

-keepattributes Signature, *Annotation*

在我的应用程序的一个经过混淆的版本中,我成功地使用了flexjson库。


3
使用Gson中的@SerializedName注解在您的模型类中时,需要添加-keepattributes *Annotation*混淆规则! - Jonik
@jonik 我正在使用 SerializeName,并且我已经添加了 -keepattributes *Annotation* 语句,但仍然出现以下错误: java.lang.AbstractMethodError: abstract method "void com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$a.a(com.google.gson.stream.JsonWriter, java.lang.Object)" at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:200) at com.google.gson.Gson.toJson(Gson.java:546 - Shajeel Afzal

4
我遇到了使用Proguard后Model类出现的错误。如果您查看GSON Proguard ,您会发现有一行代码。
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }

com.google.gson.examples.android.model.替换为您自己的model包,同样在我的情况下,我用-keep class com.consumer.myProject.model.** { *; }替换了它。

其余部分按原样复制。


2
所以,我最终放弃了Gson库,不再使用gson将对象转换为json,而是创建了一个自定义类来序列化和反序列化对象并存储数据。虽然花费了我12个小时来尝试解决问题,但总的来说我更加满意。显然,PROGUARD和Gson可能不太兼容? 最大的好处是不再使用GSON,我注意到去掉GSON库后,我的应用程序大小减半了。在删除gson库之前,我的应用程序大小为577kb,现在仅为260kb。

gson在运行时使用反射动态地将JSON映射到类中,通过字符串匹配属性。这在混淆源代码时永远不会起作用,因为model.name被Proguard替换为a.b。这与GSON本身关系较小,更多地与任何以这种方式使用反射的库有关。为了在GSON中使用proguard,您需要在proguard配置文件中排除正在序列化/反序列化的类。 - Crake
8
欢迎来到2014年,这里的577Kb和260Kb一样微不足道。 - Dmitry Zaytsev

1

看起来你确实保留了类中的所有内容(字段、方法和类本身)。但是为了确保,在 proguard.cfg 文件中添加 -printseeds outputfile.txt 可以验证一旦混淆完成,ProGuard 是否确实保留了你需要的所有内容。

顺便说一句,你可以考虑添加类似 ACRA 或 Android 远程堆栈跟踪 的东西,这样可以在构建的应用程序上检查堆栈跟踪。


谢谢,我检查了我的种子文件,它包含以下内容:'org.scanner.scanlog.SingleFrame org.scanner.scanlog.SingleFrame: int Position org.scanner.scanlog.SingleFrame: int Time org.scanner.scanlog.SingleFrame: java.util.Map MainDataMap org.scanner.scanlog.SingleFrame: java.util.Map DataMap org.scanner.scanlog.SingleFrame: SingleFrame(int,int,java.util.Map,java.util.Map)' - Jesse
也许与 java.util.Map 的混淆有关?但不确定该怎么办? - Jesse

0

除了其他答案之外,如果你们来到这里,那就意味着你想混淆你的代码并使用Gson。

如果最终你选择-keep class你的实例,这意味着这些Gson类将不会被混淆,而这个解决方案(甚至是Gson本身)对你来说并不是最优的解决方案。

在这种情况下,我建议自己序列化类并存储它们请参见@Jessy的答案


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,