ProGuard and Gson on Android (ClassCastException)

24,806

Solution 1

For the latest version of the recommended proguard configuraiton file, please see the gson supplied android proguard example at: https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg

Solution 2

These settings in the config worked for me in one of my apps:

# 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

Hopefully this helps you out.

Solution 3

Applying the changes found in the Android example in the Gson project worked for me

The lines needed were:

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

Solution 4

I know the original question was resolved by taking a different approach, but I was having a very similar issue using flexjson and Proguard on Android, and I've solved it, in case anyone runs into it themselves.

When converting back from JSON to my value object which included some ArrayLists, I would get the same ClassCastException. I got it to work by basically having obfuscation enabled but turning all parts of obfuscation off (-keep everything, -keepclassmembers everything and -keepattributes everything) and then working backwards by enabling things a bit at a time.

The result; keeping the entire flexjson library:

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

and keeping the Signature and Annotation attribute:

-keepattributes Signature, *Annotation*

I was able to use the flexjson library without incident after that in a proguarded, release version of my app.

Solution 5

I was getting errors for Model classes with proguard If you look at

GSON Proguard you will find a line

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

replace com.google.gson.examples.android.model. with your model package likewise in my case i replaced it with -keep class com.consumer.myProject.model.** { *; }

rest I copied as such

Share:
24,806
Jesse
Author by

Jesse

Updated on January 26, 2021

Comments

  • Jesse
    Jesse over 3 years

    I am having a hell of a time with Gson and ProGuard. I have a simple object and when I parse tojson, save to sqllite and read back from the database in order to load the json back to my object, I get a java.lang.classcastexception. If I dont use ProGuard, everthing works fine.

    I have verified that the json string being sent to and gotten from the database is the same. The exception is not thrown when it converts from json, but rather when I try to access the object.

    Here is my simple object:

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

    All other aspects of my app are fine, but something with proguard is causing this to happen....Ive tried all kinds of -keep commands in the proguard.cfg but I am not sure what Im doing is right.

    EDIT - ADDING 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.** { *; }
    

    EDIT - Okay I got ACRA set up successfully in my app, pretty awesome feature! Here is the stack trace:

    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)
    
  • Jesse
    Jesse almost 13 years
    Thanks,I checked my seeds file and it has the following: '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
    Jesse almost 13 years
    Maybe it has something to do with the obfuscation of java.util.Map? Not sure what to do about it though?
  • blindstuff
    blindstuff almost 13 years
    This worked for me, but I had to add: -keepattributes Signature
  • Murat
    Murat about 12 years
    The lines you mentioned was not enough for me but i applied the things in the example you provied and worked for me. Thank you.
  • Trinimon
    Trinimon about 11 years
    "Add any classes the interact that interact with gson" that did it for me, thanks!
  • Crake
    Crake almost 11 years
    gson uses reflection at runtime to dynamically map the json into classes by string matching the properties. This will never work with obfuscated sources, since model.name is replaced with a.b by Proguard. It has less to do with GSON specifically and more to do with any library that uses reflection in this manner. In order to use proguard with GSON, you need to exclude the classes you are serializing / deserializing from obfuscation in the proguard config file.
  • Jonik
    Jonik over 10 years
    +1. If using e.g. @SerializedName annotation from Gson in your model classes, -keepattributes *Annotation* is necessary!
  • umesh
    umesh about 10 years
    @jerry # Add any classes the interact with gson, it works for me but when i applied reverse engg on apk, i got all the model classes with not change in members, i am able to decompile all model, it breaks the security purpose, isn't there any other option ?
  • Matt Accola
    Matt Accola about 10 years
    @blindstuff You shouldn't add -libraryjars. In this document, proguard.sourceforge.net/index.html#manual/troubleshooting.h‌​tml, it says "You should never explicitly specify the input jars yourself (with -injars or -libraryjars), since you'll then get duplicate definitions."
  • Dmitry Zaytsev
    Dmitry Zaytsev over 9 years
    Welcome to 2014 where 577Kb and 260Kb are equally nothing.
  • Shajeel Afzal
    Shajeel Afzal over 9 years
    But i am getting the following exception with Exported apk with ProGuard: Caused by: java.lang.AbstractMethodError: abstract method "void com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$a‌​.a(com.google.gson.s‌​tream.JsonWriter, java.lang.Object)" at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$A‌​dapter.write(Reflect‌​iveTypeAdapterFactor‌​y.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
    Shajeel Afzal over 9 years
    @jonik i am using SerializeName and i have added -keepattributes *Annotation* statement but still i am getting: java.lang.AbstractMethodError: abstract method "void com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$a‌​.a(com.google.gson.s‌​tream.JsonWriter, java.lang.Object)" at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$A‌​dapter.write(Reflect‌​iveTypeAdapterFactor‌​y.java:200) at com.google.gson.Gson.toJson(Gson.java:546
  • Shajeel Afzal
    Shajeel Afzal over 9 years
    I think if someone wants to keep all classes in the Models package he should write: -keep class com.someapp.android.models.** { *; }