Избежать потери данных из-за прерывания при сохранении файлов на Android?

Мне интересно, какие стратегии используют другие, чтобы избежать потери данных при сохранении файлов на Android. У меня есть несколько игровых приложений, которые, по сути, потенциально могут сохранить состояние игры/сохранить игру всякий раз, когда пользователь делает паузу (onPause).

Это работает в 99,99% случаев, но каждый раз в то время я получаю пример savegame, в котором процесс сохранения поступил неправильно. Обычно это плохо сформированный XML файл, обычно усеченный в некоторой произвольной точке. Основываясь на отчетах об ошибках, которые я получаю, я считаю, что проблема в основном возникает, когда пользователь прерывается во время игры с помощью телефонного звонка или что-то в этом роде, а ОС Android затем убивает приложение, прежде чем он сможет завершить сохранение. Код для сохранения файлов довольно прост, поэтому мне трудно понять, что еще может вызвать эту проблему.

Это серьезная проблема, потому что это обычно приводит к тому, что игрок сохраняет прогресс, который разрушается.

Я думал сначала писать в пустой файл, а затем копировать в "настоящий" файл только после этого, но я подозреваю, что это просто увеличит проблему, так как в целом это займет больше времени и все равно рискует прерваться.

Любой, у кого есть безопасный способ сделать это, что Android относительно гарантированно не испортить?


Итак, все варианты, предложенные до сих пор (как я их понимаю):

  • Используйте службу для процесса сохранения, исходя из предположения, что она будет менее вероятна для уничтожения ОС.
  • Сохранить во временном файле; копировать, когда сохранение подтверждено.
  • Инкрементное сохранение состояния игры (я на самом деле уже использую это для информации журнала игрока).
  • Комбинация 2 и 3.
  • Переместите сохранение в другой поток, так как проблема может быть с ANR убивает [комментарии DC ниже].

Я не думаю, что SharedPreferences будут работать для такого рода структурированных данных. На данный момент ни один из этих методов не кажется идеальным решением, поэтому я по-прежнему открыт для предложений.


Мне еще не удалось проверить все эти подходы, поэтому вместо того, чтобы тратить деньги, я назначил его на ответ, который, по моему мнению, скорее всего решит проблему. Тем не менее, я планирую проверить различные варианты, прежде чем принимать ответ. Спасибо за все хорошие предложения.

Ответы

Ответ 1

Я думаю, что экономия небольших частей данных (а не всего состояния игры) в базе данных каждый раз, когда пользователь выполняет какое-либо действие (не только при вызове onPause()), будет лучшим в этом случае. Однако для этого может потребоваться много изменений кода.

Компромисс должен состоять в том, чтобы разделить состояние игры на более мелкие части или под-состояния (скажем, round1, round2,..., players и т.д.) и снова хранить данные в соответствующих файлах как пользователь выполняет любое действие, не дожидаясь вызова onPause(). Это значительно снизит вероятность потери и, по крайней мере, гарантирует, что пользователь не потеряет весь ход игры. Кроме того, чтобы избежать несогласованных состояний вашего приложения, которое может появиться при прерывании процесса сохранения, лучше сначала сохранить данные во временном файле, и только в случае успеха просто переименуйте файл, а не копируйте его содержимое (скажем, round1.xml.tmp переименовать в round1.xml).

Ответ 3

У нас были случайные проблемы, когда мы делали ввод/вывод (обычно записывая) в постоянное хранилище (частная файловая система во внутренней памяти) из основного потока. Обычно это не занимает много времени, но иногда необъяснимо требуется возраст (20 или 30 секунд или более). Похоже, что реализация файловой системы Android на некоторых устройствах не поддерживает одновременный доступ (см. this, поэтому ваш ввод-вывод может блокироваться, если другой процесс используя файловую систему. Если вы делаете свой ввод-вывод в основном потоке, OS может/будет убивать вашу активность, если она слишком долго блокируется. Возможно, это происходит с вами.

Из-за этой проблемы я предлагаю вам переместить все свои входы/выходы в отдельный поток (а не основной поток). Так, например, чтобы сохранить состояние игры, в onPause() вызывается метод, который сериализует состояние игры в ByteArrayOutputStream, который затем передается в отдельный поток, который должен быть записан в конечном итоге в файловую систему.

Ответ 4

Если возможно, используйте SharedPreferences для сохранения состояния игры. Ваши изменения будут совершены только после вызова commit(). Вы можете выполнить сохранение в onSaveInstanceState обратном вызове. Здесь читайте .

Ответ 5

Во-первых, я попытаюсь проверить, действительно ли приложение действительно убито без уведомления, потому что я не думаю, что это должно быть так. Приложения могут быть остановлены для чего-то вроде телефонного звонка, но я действительно не думаю, что андроид просто убивает его без какого-либо уведомления. Он может решить закончить его, но не без сохранения состояния и т.д.

Возможно, есть ошибка только с самой сохранением.

Способ, который не требует большой модификации, чтобы проверить, что будет использовать контрольную сумму, или использовать другой способ проверки целостности сохраненных данных (размер, конечный маркер и т.д.). Все, что вам нужно сделать, это сохранить его, прочитать и проверить (запишите любые ошибки) и повторите это до тех пор, пока оно не будет правильно. Возможно, это не так хорошо, если вы сохраняете большие объемы данных, но для простых gamedata все должно быть в порядке.

Вы также можете установить состояние, когда приложение запустится и закончится, а затем вы узнаете о следующем запуске, если он закончится правильно в последний раз или нет, и если вам нужно принять дополнительные меры.