How to stop ProGuard from stripping the Serializable interface from a class
Asked Answered
P

2

17

Is there an explicit way to stop ProGuard from changing a class from implementing an interface?

I have a class that implements java.io.Serializable, let's call it com.my.package.name.Foo. I've found that after running through ProGuard, it no longer implements Serializable. I get null after I cast from Serializable to Foo and false if I check an instance with instanceof Serializable. I've made sure to set ProGuard to ignore this class:

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

I've also tried:

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

and I've also tried the whole package by doing this:

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

or:

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

and also just to keep all Serializable classes:

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

but to no avail. I have another class in a sibling package (roughly: com.my.package.name2.Bar) that also implements Serializable and is used similarly but has no issues.

I'm not sure it's relevant, but I'm packing this in a jar for use with Android. The code that uses these classes include putting them in Bundles which is why I need Serializable. I considered that perhaps somehow ProGuard thinks that Foo is never used as a Serializable but that seems unlikely given that I pass it as a parameter to Bundle.putSerializable(String, Serializable) and also I do an implicit cast: Serializable serializable = foo;. In fact, when I debug, I can see the Foo get put into the Bundle and I can examine the Bundle and see the instance of Foo there, but when retrieving it the cast fails.

Ptero answered 18/4, 2013 at 1:41 Comment(0)
H
6

ProGuard doesn't ever strip interfaces that are defined in libraries (like Serializable) from classes in code that is processed (like Foo). Library code may be casting to those interfaces, so they can't be removed.

I get null after I cast from Serializable to Foo

This means that the instance must be null to start with. Your analysis would be correct if you'd get a ClassCastException. You can check that Foo still implements Serializable with javap. The problem probably lies elsewhere. For tips on serialization, you can look at the ProGuard manual > Examples > Processing serializable classes.

Update:

In this case, it turns out to be a configuration problem. ProGuard can only process class files if it knows everything about their hierarchy (just like a compiler). You really have to specify the runtime classes:

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

or for Android:

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

The Android Ant/Eclipse builds automatically specify all necessary -injars/-outjars/-libraryjars options for you, but in a custom build process, you have to specify them yourself. Cfr. ProGuard manual > Examples > A complete Android application.

Note that the option -dontwarn makes the warnings go away, but not the problems. Only use it if really necessary.

Hexose answered 20/4, 2013 at 22:42 Comment(3)
Thanks for the input, but I've checked with javap and Foo doesn't implement Serializable anymore. Your comment about ClassCastException makes sense to me too, but it just doesn't seem to work that way. My suspicion is that something has happened in the shrinking and optimization to change the behavior from what you might expect normal Java code to do as at that point it's no longer Java, but byte-code.Ptero
I've found that if I have ProGuard set to -dontshrink and then explicitly use -keep,allowoptimization,allowshrinking,allowobfuscation Foo then Foo will still implement Serializable, but unfortunately that's not a solution for me as I'd like shrink to be on in general.Ptero
If you have a small sample that illustrates the problem, I'll look into it. You can find my mail address on the Feedback page of the ProGuard website.Hexose
T
48

I had the same issue fixed using below config.

-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();
}

Official Documentation http://proguard.sourceforge.net/manual/examples.html#serializable

Tincture answered 29/5, 2015 at 14:49 Comment(2)
In my opinion, this should have been a default rule in proguard.Claypan
Thank you! I've spent so much time trying to get this to work.Churchy
H
6

ProGuard doesn't ever strip interfaces that are defined in libraries (like Serializable) from classes in code that is processed (like Foo). Library code may be casting to those interfaces, so they can't be removed.

I get null after I cast from Serializable to Foo

This means that the instance must be null to start with. Your analysis would be correct if you'd get a ClassCastException. You can check that Foo still implements Serializable with javap. The problem probably lies elsewhere. For tips on serialization, you can look at the ProGuard manual > Examples > Processing serializable classes.

Update:

In this case, it turns out to be a configuration problem. ProGuard can only process class files if it knows everything about their hierarchy (just like a compiler). You really have to specify the runtime classes:

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

or for Android:

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

The Android Ant/Eclipse builds automatically specify all necessary -injars/-outjars/-libraryjars options for you, but in a custom build process, you have to specify them yourself. Cfr. ProGuard manual > Examples > A complete Android application.

Note that the option -dontwarn makes the warnings go away, but not the problems. Only use it if really necessary.

Hexose answered 20/4, 2013 at 22:42 Comment(3)
Thanks for the input, but I've checked with javap and Foo doesn't implement Serializable anymore. Your comment about ClassCastException makes sense to me too, but it just doesn't seem to work that way. My suspicion is that something has happened in the shrinking and optimization to change the behavior from what you might expect normal Java code to do as at that point it's no longer Java, but byte-code.Ptero
I've found that if I have ProGuard set to -dontshrink and then explicitly use -keep,allowoptimization,allowshrinking,allowobfuscation Foo then Foo will still implement Serializable, but unfortunately that's not a solution for me as I'd like shrink to be on in general.Ptero
If you have a small sample that illustrates the problem, I'll look into it. You can find my mail address on the Feedback page of the ProGuard website.Hexose

© 2022 - 2024 — McMap. All rights reserved.