34

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)
Jonik
  • 80,077
  • 70
  • 264
  • 372
Jesse
  • 2,674
  • 6
  • 30
  • 47
  • What's the exact error message, and what did you put in your proguard.cfg? – THelper Jun 30 '11 at 13:43
  • I added it in. Since I cant debug a built app, I added toast messages in all the pertinent catch blocks. The toast message right where I try to access the object re-created from json is: java.lang.ClassCastException: java.lang.object – Jesse Jun 30 '11 at 13:52
  • 1
    You use [this](http://code.google.com/p/google-gson/source/browse/trunk/examples/android-proguard-example/proguard.cfg?r=878) proguard configuration. – Macarse Aug 25 '11 at 17:47

8 Answers8

70

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

Dmytro Danylyk
  • 19,684
  • 11
  • 62
  • 68
Guy
  • 12,250
  • 6
  • 53
  • 70
  • 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.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)` – Shajeel Afzal Feb 01 '15 at 12:06
26

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.

Stokedbits
  • 425
  • 3
  • 13
  • 4
    This worked for me, but I had to add: `-keepattributes Signature` – blindstuff Sep 01 '11 at 18:29
  • 4
    *"Add any classes the interact that interact with gson"* that did it for me, thanks! – Trinimon Apr 26 '13 at 11:11
  • @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 ? – umesh Apr 30 '14 at 09:10
  • @blindstuff You shouldn't add -libraryjars. In this document, http://proguard.sourceforge.net/index.html#manual/troubleshooting.html, it says "You should never explicitly specify the input jars yourself (with -injars or -libraryjars), since you'll then get duplicate definitions." – Matt Accola May 05 '14 at 14:46
  • 4
    I think if someone wants to keep all classes in the Models package he should write: `-keep class com.someapp.android.models.** { *; }` – Shajeel Afzal Feb 01 '15 at 13:14
14

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
Daddyboy
  • 1,412
  • 13
  • 19
  • 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. – Murat Jun 26 '12 at 04:08
6

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.

HTLD
  • 61
  • 2
  • 3
    +1. If using e.g. `@SerializedName` annotation from Gson in your model classes, `-keepattributes *Annotation*` is necessary! – Jonik Jan 22 '14 at 13:39
  • @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.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` – Shajeel Afzal Feb 01 '15 at 13:05
4

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

DeltaCap019
  • 6,532
  • 3
  • 48
  • 70
2

So, I ended up ditching the Gson library and instead of converting my object to json using gson, I created a custom class in my app to serialize and deserialize the object and store the data that way.

I am overall more happy, even though this has cost me over 12 hours of trying to figure it out. Apparently, PROGUARD and gson must not like each other too much?

As A HUGELY added benefit to not having to use GSON, I noticed that by taking out the GSON library, my app size is cut in half. My app was 577kb and is now only 260kb after removing the gson lib.

Jesse
  • 2,674
  • 6
  • 30
  • 47
  • 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. – Crake Sep 24 '13 at 16:41
  • 8
    Welcome to 2014 where 577Kb and 260Kb are equally nothing. – Dmitry Zaytsev Dec 04 '14 at 15:48
1

It does look like your are keeping everything from your class (fields, methods and the class itself). But to make sure you can add -printseeds outputfile.txt to the proguard.cfg file to verify that proguard really keeps everything you need once obfuscation is done.

BTW, you might think about adding something like ACRA or Android Remote stacktrace that allows you to inspect stacktraces on a built app.

Community
  • 1
  • 1
THelper
  • 15,333
  • 6
  • 64
  • 104
  • 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 Jun 30 '11 at 19:05
  • Maybe it has something to do with the obfuscation of java.util.Map? Not sure what to do about it though? – Jesse Jun 30 '11 at 19:10
0

Just to add to all of the other answers, if you guys reached here, it means that you want to obfuscate your code AND use Gson.

If, at the end, you chose to -keep class your instances, it means that these Gson classes WILL NOT BE OBFUSCATED and this solution (or even Gson in general, is not an optimal solution for you).

In this case I would advise to Serialize the classes yourself and store them See @Jessy's answer.

Oz Shabat
  • 1,434
  • 17
  • 16