1

My Proguard release builds are stripping out my initialization values of my member fields. This is causing my default values to get lost, resulting in different behavior between my debug and release builds. How can I stop this happening?

Here's my source code

public class MyClass implements Serializable {
    private static final long serialVersionUID = 1L;

    @SerializedName("myField")
    private boolean myFieldEnabled = true;

    public boolean isMyFieldEnabled() {
        return myFieldEnabled;
    }

    public void setMyFieldEnabled(boolean myFieldEnabled) {
        this.myFieldEnabled = myFieldEnabled;
    }

    ...

The obfuscated code looks like this (the setters and getters are removed):

public final class ai implements Serializable
{
    private static final long serialVersionUID = 1L;
    @z(x="myField")
    public boolean myFieldEnabled;

Here's a snippet from my build.gradle:

release {
    minifyEnabled true
    shrinkResources false
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'custom-proguard-rules-optimize.pro'
}

Here are the key Proguard optimization rules from my custom rules (from custom-proguard-rules-optimize.pro):

# Disable the "code/allocation/variable" optimizations to workaround a
# Proguard bug.  Source: http://stackoverflow.com/a/7587680/112705
-optimizations !code/simplification/arithmetic,field/removal/writeonly,!class/merging/*,!code/allocation/variable
-allowaccessmodification

Here are the key Proguard optimization rules from the Android default rules that we are using (from proguard-android-optimize.txt):

-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5

I checked the Proguard optimizations documentation and I couldn't see an optimization or obfuscation option that covered this.

Updates

  • Adding !field/* into our optimizations did not help - the field still did not seem to get initialized. Hopefully this optimization was already being picked up from the Android default Proguard rules...
  • Removing field/removal/writeonly did not help.
  • Adding !code/simplification/field did not help.
Dan J
  • 25,433
  • 17
  • 100
  • 173
  • Start with using "proguard-android.txt" instead of "proguard-android-optimize.txt". In the file it even says that dex does some optimizations by itself and doesn't agree with the optimized proguard rules. – Eugen Pechanec Sep 15 '15 at 22:20
  • I'd like to keep using Proguard optimizations. – Dan J Sep 16 '15 at 13:40
  • As I said dex does some optimizations anyway. I suggest you try using the non optimized version of proguard rules and if it produces correct build work from there. ||| In the original optimized rules there is `!field/*`, aren't you missing an exclamation point in your rules? – Eugen Pechanec Sep 16 '15 at 19:40
  • Try adding this proguard snippet for Gson. https://github.com/krschultz/android-proguard-snippets/blob/master/libraries/proguard-gson.pro – Jared Rummler Sep 22 '15 at 21:07
  • How do you see code after obfuscation since the proguard obfuscates the class files? – Eugen Martynov Sep 29 '15 at 17:52

2 Answers2

2

I've tried to obfuscate a serializable class, and I'm not getting your issue. I've used the default proguard-android-optimize.txt and your version of the optimization, and everything is fine.

Original class

public class TestSerializable implements Serializable {
    private static final long serialVersionUID = 1L;

    @SerializedName("myField")
    private boolean myFieldEnabled = true;

    public boolean isMyFieldEnabled() {
        return myFieldEnabled;
    }

    public void setMyFieldEnabled(boolean myFieldEnabled) {
        this.myFieldEnabled = myFieldEnabled;
    }
}

After proguard

public final class p implements Serializable
{
    @c(a="myField")
    private boolean a = true;

    public final boolean a()
    {
        return this.a;
    }

    public final void b()
    {
        this.a = false;
    }
}

I've three suggestions

  • try adding -keep class * implements java.io.Serializable to your proguard file and see what happens
  • verify in your code that the method setMyFieldEnabled(false) is not called right after every new MyClass, otherwise the optimization can decide to remove the default value
  • look at the obfuscated serialized class: the field myFieldEnabled is public; so it can be accessed from everywhere. Check in your obfuscated classes that are using MyClass if proguard replaced the code with something similar to ai a = new ai(); ai.myFieldEnabled = true;
Mimmo Grottoli
  • 5,758
  • 2
  • 17
  • 27
0

Try to declare default constructor for your class which implements Serializable

public class MyClass implements Serializable {
    private static final long serialVersionUID = 1L;

    @SerializedName("myField")
    private boolean myFieldEnabled;

    private MyClass() {
        myFieldEnabled = true; 
    }  

    public boolean isMyFieldEnabled() {
        return myFieldEnabled;
    }

    public void setMyFieldEnabled(boolean myFieldEnabled) {
        this.myFieldEnabled = myFieldEnabled;
    }

For default ProGaurd configuration with serailisable please see http://proguard.sourceforge.net/manual/examples.html#serializable

AndreyICE
  • 3,574
  • 29
  • 27