如何阻止 ProGuard 从类中剥离 Serializable 接口

Posted

技术标签:

【中文标题】如何阻止 ProGuard 从类中剥离 Serializable 接口【英文标题】:How to stop ProGuard from stripping the Serializable interface from a class 【发布时间】:2013-04-10 23:44:02 【问题描述】:

是否有明确的方法来阻止 ProGuard 更改类以实现接口?

我有一个实现java.io.Serializable 的类,我们称之为com.my.package.name.Foo。我发现通过 ProGuard 运行后,它不再实现Serializable。如果我使用instanceof Serializable 检查实例,我会在从Serializable 转换为Foofalse 后得到null。我确保将 ProGuard 设置为忽略此类:

-keep class com.my.package.name.Foo

我也试过了:

-keep class com.my.package.name.Foo  *; 

我也通过这样做尝试了整个包:

-keep class com.my.package.name.**  *; 

或:

-keep class com.my.package.**  *; 

也只是为了保留所有Serializable 类:

-keep class * implements java.io.Serializable  *; 

但无济于事。我在同级包中有另一个类(大致为:com.my.package.name2.Bar),它也实现了Serializable,并且使用类似但没有问题。

我不确定它是否相关,但我将它打包在一个罐子中以供 android 使用。使用这些类的代码包括将它们放在Bundles 中,这就是我需要Serializable 的原因。我认为 ProGuard 可能以某种方式认为 Foo 从未用作 Serializable,但考虑到我将它作为参数传递给 Bundle.putSerializable(String, Serializable) 并且我做了一个隐式转换:Serializable serializable = foo;,这似乎不太可能。事实上,当我调试时,我可以看到Foo 被放入Bundle 中,我可以检查Bundle 并在那里看到Foo 的实例,但是在检索它时转换失败。

【问题讨论】:

【参考方案1】:

我使用下面的配置解决了同样的问题。

-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable 
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();

官方文档 http://proguard.sourceforge.net/manual/examples.html#serializable

【讨论】:

在我看来,这应该是proguard中的默认规则。 谢谢!我花了很多时间试图让它发挥作用。【参考方案2】:

ProGuard 不会从已处理代码中的类(如 Foo)中剥离库中定义的接口(如 Serializable)。库代码可能会转换为这些接口,因此无法删除。

从 Serializable 转换为 Foo 后我得到 null

这意味着实例必须为 null 才能开始。如果您得到 ClassCastException,您的分析将是正确的。您可以使用javap 检查 Foo 是否仍然实现 Serializable。问题可能出在其他地方。有关序列化的提示,您可以查看 ProGuard 手册 > 示例 > Processing serializable classes。

更新:

在这种情况下,结果证明是配置问题。 ProGuard 只能处理类文件,如果它知道所有关于它们的层次结构(就像编译器一样)。您确实必须指定运行时类:

-libraryjars <java.home>/lib/rt.jar

或对于 Android:

-libraryjars /usr/local/android-sdk/platforms/android-17/android.jar

Android Ant/Eclipse 构建会自动为您指定所有必要的 -injars/-outjars/-libraryjars 选项,但在自定义构建过程中,您必须自己指定它们。参照。 ProGuard 手册 > 示例 > A complete Android application.

请注意,选项 -dontwarn 使警告消失,而不是问题。仅在确实需要时才使用它。

【讨论】:

感谢您的输入,但我已经检查了 javapFoo 不再实现 Serializable。您对ClassCastException 的评论对我来说也很有意义,但它似乎并没有那样工作。我怀疑在缩小和优化中发生了一些事情,以改变您可能期望普通 Java 代码执行的行为,因为此时它不再是 Java,而是字节码。 我发现如果我将 ProGuard 设置为 -dontshrink,然后明确使用 -keep,allowoptimization,allowshrinking,allowobfuscation Foo,那么 Foo 仍将实现 Serializable,但不幸的是,这不是我的解决方案,因为我'希望缩小到一般情况下。 如果你有一个说明问题的小样本,我会调查它。您可以在 ProGuard 网站的反馈页面上找到我的邮件地址。

以上是关于如何阻止 ProGuard 从类中剥离 Serializable 接口的主要内容,如果未能解决你的问题,请参考以下文章

使用 Proguard 剥离未使用的 Support lib 类

如何阻止proguard混淆整个包?

你如何阻止 Proguard 删除类型参数?

如何从类中为 EnvironmentObject 设置值?

如何从类中获取 id [重复]

如何从类中获取“ReadOnly”或“WriteOnly”属性?