I have posted a game early this month on Google Play, and got about 100K downloads.
I use Google Protobuf to organize my data, serialize it to binary code and base64 as a text.
At last I use CCUserDefault::sharedUserDefault()->setStringForKey to save and call flush every time
But Many users commented: “Lose data , and game back to the start”.
FishingJoy1 also has this bug report, but we haven’t reproduce it.
Some one said remove the battery when playing the game can cause data losing, some one said fwrite method is not reliable enough.
So finally FishingJoy team move CCUserDefault from fwrite to android.content.SharedPreferences. You can refer to this doc http://developer.android.com/reference/android/content/SharedPreferences.html. This android sdk method covered all exceptions, it works very well that we haven’t received data losing report.
But this modification hasn’t been merged into cocos2d-x framework yet. You can call android.content.SharedPreferences via JNI from CCUserDefault. Perhaps we should create a issue for this.
BTW. What’s your game? 100K downloads is no bad. Could you give me an URL?
This issue got very rare by removing the flush call from destructor of CCUserDefault, as it tries to save progress when the .so gets unloaded and this would happen if user runs out of battery while playing or if the game got killed by a “task manager” when left running background. Instead we now save the progress only when changes happen.
Still it seems that there are some users experiencing this, I’m just wondering if the xml library calls fflush after it’s done writing with fwrite. Also thinking about using the Android shared preferences…
CCUserDefault::flush* is used to save the data to disk. If not invoked, all changes to the dat will not be saved to disk.
The nameflush()* is very misleading though. When I first saw it, I thought *flush()* would erase all data on the save file.
Thought I’d post an update on my experiences with Hill Climb Racing, even though we made those minor adjustments when the flush was called, users data still got corrupted sometimes and we had lots of support contacts regarding user save data being lost. We finally decided to try the SharedPreferences approach and the support contacts about corrupted data got reduced greatly after the update, so I recommend using the SharedPreferences if possible.
Never had this issue so far on 3 cocos2dx android games released.
I do agree from a design perspective it should use SharedPreferences API for Android implementation.
Hi Toni,
Thanks for the update, I’ve decided to use Android’s shared preference too for my next game.
Did you receive reports of data corruption on iOS too? My concern there this bug can also happen in iOS.
There’s been only one report on iOS and the user said it got corrupted when the battery run out while playing. Seemingly its very rare as the game has over 10M downloads on iOS also.
Is anyone able to provide some source for the JNI interface / Android side of things? I’m looking into doing this myself now, figure why reinvent the wheel if all you guys have already done it
An important thing to note when using JNI and share preferences is that if the call is made
from a different thread the JNI call will fail and won’t work.
Take that into account if you are using threads!
Hello friends, I’m new and I’m finishing my first game, I have some concerns:
I create my own file jni?
I can not find UserDefaults wrapper
Thank you for your help.
I’ve faced the same problem with losting my saved data. So I’ve moved to JNI sharedPreferences. It works better but no so good as it should work. It losts data also but rarely than CCUserDefault. I’ve read sharePreferences at the start of my application so it never starts if saved data is corrupted. F**king XML! It’s a very big format just for storing a little data. So if you need to do commit operations very often - it brokes on Android (especially on slow devices). So it brokes all your game
Has anyone solved such problem with sharedPreferences? By the way, CCUserDefault for iOS also uses XML nowdays?
CCUserDefault works well for me, the problem is when the key is int to string ex: “1” on a for() or while() for a score board.
concatenating a letter solved
ex
for (int i = 0; i <9, i + +)
{
sprintf (var_i, “data_% d”, i);
if (data> CCUserDefault :: sharedUserDefault () -> getIntegerForKey (var_i)) // if score is greater
{
…………………….