Android 上的 ProGuard 和 Gson (ClassCastException)

Posted

技术标签:

【中文标题】Android 上的 ProGuard 和 Gson (ClassCastException)【英文标题】:ProGuard and Gson on Android (ClassCastException) 【发布时间】:2011-09-25 21:29:15 【问题描述】:

我和 Gson 和 ProGuard 玩得很开心。我有一个简单的对象,当我解析 tojson、保存到 sqllite 并从数据库中读回以便将 json 加载回我的对象​​时,我得到一个 java.lang.classcastexception。如果我不使用 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 的某些东西导致了这种情况的发生......我在 proguard.cfg 中尝试了各种 -keep 命令,但我不确定我做的是否正确。

编辑 - 添加 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 中放了什么? 我添加了它。由于我无法调试构建的应用程序,我在所有相关的 catch 块中添加了 toast 消息。我尝试访问从 json 重新创建的对象的 toast 消息是:java.lang.ClassCastException: java.lang.object 你使用this proguard 配置。 【参考方案1】:

有关推荐的 proguard 配置文件的最新版本,请参阅 gson 提供的 android proguard 示例: https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg

【讨论】:

但是我在使用 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)【参考方案2】:

配置中的这些设置在我的一个应用中对我有用:

# 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

希望这对您有所帮助。

【讨论】:

这对我有用,但我必须添加:-keepattributes Signature “添加任何与 gson 交互的类”,谢谢! @jerry # 添加任何与 gson 交互的类,它对我有用,但是当我在 apk 上应用反向 engg 时,我得到了所有成员没有变化的模型类,我能够反编译所有模型,它破坏了安全目的,没有其他选择吗? @blindstuff 您不应该添加 -libraryjars。在这个文档proguard.sourceforge.net/index.html#manual/troubleshooting.html 中,它说“你永远不应该自己明确指定输入 jars(使用 -injars 或 -libraryjars),因为这样你会得到重复的定义。” 我认为如果有人想保留 Models 包中的所有类,他应该写:-keep class com.someapp.android.models.** *; 【参考方案3】:

在 Gson 项目中应用在 Android example 中发现的更改对我有用

需要的行是:

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

【讨论】:

您提到的行对我来说还不够,但我应用了您提供的示例中的内容并为我工作。谢谢。【参考方案4】:

我知道最初的问题是通过采用不同的方法解决的,但我在 Android 上使用 flexjson 和 Proguard 时遇到了非常相似的问题,我已经解决了,以防万一有人自己遇到。

从 JSON 转换回包含一些 ArrayList 的值对象时,我会得到相同的 ClassCastException。我基本上启用了混淆,但关闭了混淆的所有部分(-keep all,-keepclassmembers Everything 和 -keepattributes all),然后通过一次启用一些东西来向后工作。

结果;保留整个 flexjson 库:

-keep class flexjson**
--keepclassmembers class flexjson** 
   *;

并保留 Signature 和 Annotation 属性:

-keepattributes Signature, *Annotation*

在那之后,我能够在我的应用程序的经过保护的发布版本中使用 flexjson 库而不会发生任何意外。

【讨论】:

+1。如果使用例如@SerializedName 在模型类中来自 Gson 的注释,-keepattributes *Annotation* 是必需的! @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【参考方案5】:

如果你看一下,我会收到带有 proguard 的模型类的错误

GSON Proguard你会发现一行

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.**  *; 

用你的model包替换com.google.gson.examples.android.model.同样在我的情况下我用-keep class com.consumer.myProject.model.** *; 替换它

其余我照此复制

【讨论】:

【参考方案6】:

所以,我最终放弃了 Gson 库,而不是使用 gson 将我的对象转换为 json,而是在我的应用程序中创建了一个自定义类来序列化和反序列化对象并以这种方式存储数据。

我总体上更快乐,尽管这让我花了 12 多个小时试图弄清楚。看来PROGUARD和gson应该不会太喜欢对方吧?

作为一个巨大的额外好处,不必使用 GSON,我注意到通过取出 GSON 库,我的应用程序大小减少了一半。我的应用是 577kb,删除 gson 库后现在只有 260kb。

【讨论】:

gson 在运行时使用反射通过匹配属性的字符串将 json 动态映射到类中。这将永远不会与混淆的源代码一起使用,因为 Proguard 将 model.name 替换为 a.b。它与 GSON 的关系不大,而与任何以这种方式使用反射的库有关。为了在 GSON 中使用 proguard,您需要在 proguard 配置文件中排除您正在序列化/反序列化的类。 欢迎来到 577Kb 和 260Kb 的 2014 年。【参考方案7】:

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

顺便说一句,您可能会考虑添加类似ACRA or Android Remote stacktrace 的内容,以允许您检查已构建应用程序的堆栈跟踪。

【讨论】:

谢谢,我检查了我的种子文件,它有以下内容: '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)' 可能跟java.util.Map的混淆有关?不知道该怎么办?【参考方案8】:

只是添加到所有其他答案,如果你们到达这里,这意味着你想混淆你的代码并使用 Gson。

如果最后你选择-keep class你的实例,这意味着这些 Gson 类不会被混淆并且这个解决方案(或者甚至是一般的 Gson,不是最佳解决方案给你)。

在这种情况下,我建议您自己序列化这些类并将它们存储在See @Jessy's answer。

【讨论】:

以上是关于Android 上的 ProGuard 和 Gson (ClassCastException)的主要内容,如果未能解决你的问题,请参考以下文章

新(空白)项目上的尴尬 Proguard 错误

三星设备上的 Proguard“缺少类型参数”

使用 ProGuard,对测试策略有何影响?

Android - Proguard 和改造 2?

proguard.cfg 文件丢失

TSO和GSO