Rare Android users lose game data

Some rare users of my game are complaining about lose data when turn off the device.

The game is for Android, and uses Cocos version 3.17.2 (but the problem occurs since first version of the game, 3.5.0)

I’m using FileUtils::getInstance()->getWritablePath() and fopen/fwrite/fclose to save the file

The game saves the file 2 times, to avoid some weird problem if the device runs out of battery in the moment of saving a file and the file is saved corrupted. (So, when in the moment of reading the file, if it not exists or checksum is not ok, I load the other file saved), but this doesn’t solve the problem.

On my personal devices, FileUtils::getInstance()->getWritablePath() AND FileUtils::sharedFileUtils()->getWritablePath() are the same, could have difference in some other device ?

I call FileUtils::getInstance()->getWritablePath() every time I write the file, the value received can change in the same device ?

On my personal devices, fflush() causes no difference. In some devices it can make differecence if I call fopen/fwrite/fflush/fclose ?

Thanks in advance if anyone can help.

Why don’t you just use the low battery event and write your data out then?

https://stackoverflow.com/questions/10306790/android-do-something-when-battery-is-at-a-defined-level

If you inspected the code, you would have seen this in CCFileUtils.h:
CC_DEPRECATED_ATTRIBUTE static FileUtils* sharedFileUtils() { return getInstance(); }

So, yes, they are exactly the same.

If your issue is only happening when they turn off the device, then I’m assuming you mean the device is being turned off while your app is running, or is still in the background. You can always save any required data when Android notifies your app that it’s going to be closed:

onSavedInstanceState
https://developer.android.com/guide/components/activities/activity-lifecycle.html#asem
https://developer.android.com/topic/libraries/architecture/saving-states

Writing your data out twice won’t help, especially if the data is being written to the same storage device, since any problem is going to affect both your outputs. You’re not addressing the actual underlying problem, and if you don’t know what it is, then it may serve you best to focus your efforts into figuring it out.

Thanks for the answer.

I have no idea what is happening, I can’t reproduce the problem. Rare people (1 / 1000) say “I lost my data”, and that is almost all I have, some of them relate the problem occurs when device is turned off.

This was an attempt to solve or decrease the problem. This works well for my games in Windows/Linux/Embedded machines (it’s a common technique because the power can run out without warning), so I tried to implement that on Android. Because, if the battery runs out at the time of saving a file (maybe at this moment of saving the device consumes more energy) it would only damage the file being saved, the other file previously saved would be ok.

At least, after that I understood the problem is not that (saving a file during battery runs out), because the problem didn’t disappear. I don’t get an old saved situation, the data are all lost.

On Windows or Linux, a simple fflush / fclose are not enough to guarantee the file is really being saved physically on disk, you need more commands to force that. Do you know if there is something else necessary to guarantee the data is is really saved on Android, and not only on a cache? Anyway, I will try fsync() in my next version.

I talked to another guy in the forum, having the same problem. He can’t reproduce the problem too. But he put traces within the code and sending it to his stat server, and sometimes the save was failing returning error code “unable to save file”, sometimes the save was working but only portion of the save file was actually being saved, the remaining was garbage and so invalidating the save file. The problem rarely occurs with him too.

I think the problem occurs rarely with other games too, but it is not noticed.

You would generally get the warning at around 10% battery life, and that is enough to power the device for a considerable period of time, and more than enough to finish writing anything to storage.

Have you considered writing data to a different location than the default one provided by getWritablePath()? More info here: https://developer.android.com/training/data-storage

Also, is there any chance that the user device may be low on storage space? Perhaps you can do a check on the space available before saving, and if there isn’t enough space, warn the user.

Are you verifying the data after writing it to storage? If not, maybe it’s a good idea to do so directly after you write the data, because at least then you will know if it was written correctly or not, and can prompt the user to try again.

Do you have event logging in your app? It would help to log certain events so you know exactly when and why they occurred. If you don’t have a logging system in place, then consider adding one, specifically one that outputs to a remote server. Otherwise, add an option in your app to allow the user to forward the event log to you in some way.

The other user may be having a completely different issue, and there is no way to know without answering many questions, like what is the size of the data that should be written, and how much is actually getting written? Is the functionality run in a separate thread? What methods are being used to write the data?

The most important thing is figuring out what is the root cause of this problem.