1

This is a follow-on question from here: Save Bundle to SharedPreferences

I am trying to use the ComplexPreferences class from the aforementioned thread to save a Bundle object in my Android app, but to no avail! Here is the code I'm using to save and load the data to ComplexPreferences (much the same as in the example on the download page for the ComplexPreferences class):

Class1:

save = ComplexPreferences.getComplexPreferences(this, SAVE, MODE_PRIVATE);
...
Bundle gameState = new Bundle();
saveGameData(gameState); // This puts the game data into the Bundle
save.putObject(SAVE, gameState);
save.commit();

Class2:

save = ComplexPreferences.getComplexPreferences(this, Class1.SAVE, MODE_PRIVATE);
...
Bundle gameState = save.getObject(Class1.SAVE, Bundle.class);

The problem appears to be when saving the object to ComplexPreferences. Here is the stack trace I get when running the above code:

FATAL EXCEPTION: main java.lang.RuntimeException: Unable to pause activity {djb.ampersands/djb.ampersands.Ampersands}: java.lang.IllegalStateException: circular reference error Offending field: game Offending object: preserveType: false, type: class djb.ampersands.Ampersands, obj: djb.ampersands.Ampersands@41272778 at android.app.ActivityThread.performPauseActivity(ActivityThread.java:2838) at android.app.ActivityThread.performPauseActivity(ActivityThread.java:2794) at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:2772) at android.app.ActivityThread.access$800(ActivityThread.java:130) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1206) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4745) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) at dalvik.system.NativeStart.main(Native Method) Caused by: java.lang.IllegalStateException: circular reference error Offending field: game Offending object: preserveType: false, type: class djb.ampersands.Ampersands, obj: djb.ampersands.Ampersands@41272778 at com.google.gson.JsonSerializationVisitor.visitObjectField(JsonSerializationVisitor.java:117) at com.google.gson.ReflectingFieldNavigator.visitFieldsReflectively(ReflectingFieldNavigator.java:69) at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:120) at com.google.gson.JsonSerializationVisitor.getJsonElementForChild(JsonSerializationVisitor.java:147) at com.google.gson.JsonSerializationVisitor.addAsChildOfObject(JsonSerializationVisitor.java:127) at com.google.gson.JsonSerializationVisitor.visitObjectField(JsonSerializationVisitor.java:114) at com.google.gson.ReflectingFieldNavigator.visitFieldsReflectively(ReflectingFieldNavigator.java:69) at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:120) at com.google.gson.JsonSerializationVisitor.getJsonElementForChild(JsonSerializationVisitor.java:147) at com.google.gson.JsonSerializationVisitor.addAsChildOfObject(JsonSerializationVisitor.java:127) at com.google.gson.JsonSerializationVisitor.visitObjectField(JsonSerializationVisitor.java:114) at com.google.gson.ReflectingFieldNavigator.visitFieldsReflectively(ReflectingFieldNavigator.java:69) at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:120) at com.google.gson.JsonSerializationContextDefault.serialize(JsonSerializationContextDefault.java:62) at com.google.gson.BaseMapTypeAdapter.serialize(BaseMapTypeAdapter.java:34) at com.google.gson.MapTypeAdapter.serialize(MapTypeAdapter.java:53) at com.google.gson.MapTypeAdapter.serialize(MapTypeAdapter.java:33) at com.google.gson.JsonSerializationVisitor.findAndInvokeCustomSerializer(JsonSerializationVisitor.java:184) at com.google.gson.JsonSerializationVisitor.visitFieldUsingCustomHandler(JsonSerializationVisitor.java:204) at com.google.gson.ReflectingFieldNavigator.visitFieldsReflectively(ReflectingFieldNavigator.java:63) at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:120) at com.google.gson.JsonSerializationContextDefault.serialize(JsonSerializationContextDefault.java:62) at com.google.gson.JsonSerializationContextDefault.serialize(JsonSerializationContextDefault.java:53) at com.google.gson.Gson.toJsonTree(Gson.java:220) at com.google.gson.Gson.toJson(Gson.java:260) at com.google.gson.Gson.toJson(Gson.java:240) at djb.ampersands.gui.ComplexPreferences.putObject(ComplexPreferences.java:47) at djb.ampersands.Ampersands.onPause(Ampersands.java:93) at android.app.Activity.performPause(Activity.java:5106) at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1225) at android.app.ActivityThread.performPauseActivity(ActivityThread.java:2825) ... 12 more Caused by: com.google.gson.CircularReferen

Since the object was saved incorrectly, it makes sense that I also get an error when trying to load the object again:

java.lang.IllegalArgumentException: Object storaged with key save is instanceof other class at djb.ampersands.gui.ComplexPreferences.getObject(ComplexPreferences.java:63)

Summary: When I first wrote this question, I said I couldn't get any objects to save/load correctly. However, I have now got this method to work for some objects, so really my question now is, can it ever work for Bundle objects, and the answer, it would appear, is no... so I guess it's back to the drawing board!

Community
  • 1
  • 1
Dan
  • 1,198
  • 4
  • 17
  • 34
  • when you save your game data *and* open your prefs file, it looks like you use this *same* (which i don't recommend) key `SAVE` But when you get your object back, it looks like you're using two different keys `Ampersands.SAVE` and `Class1.SAVE`. my worry is that one or more of these new values might not represent the same String value that you saved them with. I think you should try not using the `static` for a bit and just use plain `"string"` to test. moreover, serialization is still an option ^^ – mango Dec 02 '12 at 04:39
  • It's hard as you seem to obfuscate a lot of things. I mean you keep your class structures secret and don't show the stack trace. But we need those informations and I don't think they would be leaking any secret... – Snicolas Dec 02 '12 at 07:10
  • Coudl you try : Object gameState = save.getObject(Ampersands.SAVE, Object.class); and check the Class of gameState. – Snicolas Dec 02 '12 at 07:12
  • Sorry, `Ampersands` is the same as `Class1`, I just changed it for simplicity but evidently forgot to replace it in one instance. I didn't mean to confuse things by obfuscating anything. There's no big secret to the class structure, they're just 2 separate Activities. I will try your suggestions and edit my question with the stack trace, but by the sounds of things, Bundles may just be too complex for Json to handle :( – Dan Dec 02 '12 at 14:09

1 Answers1

3

You should not save your bundle entirely but only your gameState object. Bundles might be more complex to serialize than a simple POJO.

As the lib uses Json to serialize things, it expects a simple POJO and Bundles are definitely not simple POJOs : some fields of the Bundle class will never be serialized correctly in Json, especially the class loader.

Snicolas
  • 37,840
  • 15
  • 114
  • 173
  • My gameState object IS a Bundle, so if Bundles cannot be saved in this way then I guess I'm back to my orginial question. Thank you all the same! – Dan Dec 02 '12 at 14:46