Ошибка файловой операции при сбое / уничтожении игры ОС / пользователем в Android

13

Моя игра сохраняет свое состояние после фиксированного интервала в файле во внутренней памяти игры / приложения. Когда моя игра убивается или сбивается пользователем или ОС соответственно, мы записываем текущее состояние игры в этот файл. Запись файла по истечении интервала исправления работает нормально, но когда игра падает или уничтожается ОС или пользователем, операция записи файла не выполняется. Сбой операции приводит к неполному игровому состоянию или пустому игровому состоянию, т. Е. Данные не записываются в файл. Я пробовал несколько решений, использующих сервис Android, в случае, если сервис не является липким сервис убит приложением. С другой стороны, если служба STICKY, она перезапускается, но намерение, которое изначально было связано с этой службой, является нулевым или новым.

Вопрос в том, как я могу полностью сохранить свои данные (может быть ~ 2-3 МБ) в файле во внутреннем хранилище, когда моя игра / приложение уничтожено пользователем / ОС?

Фейсал Имран
источник
Вы должны справиться со своими SIGTERM и своими SIGKILL (ну, может быть, не SIGKILL, Android, вероятно, использует SIGTERM). (У меня нет времени исследовать / написать полный ответ, но это основная идея.)
Джон Гамильтон
2
@JohnHamilton SIGKILL не может быть обработан по определению.
Darkhogg
@ Darchogg Ну, не в Unity, это точно. (Я когда-то менял ядро ​​linux для проекта для этой конкретной цели, и, безусловно, можно позволить программам обрабатывать SIGKILL)
Джон Гамильтон

Ответы:

20

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

Сохранить:

  • Если файлов не существует, запишите состояние в файл A
  • Если A существует, напишите B
  • Если A и B существуют, найдите, какой из них старше, удалите его, а затем запишите в него

Загрузить:

  • Если существуют и A, и B, попробуйте загрузить более новую из двух. Если это не удается (поскольку файл является неполным или поврежденным), удалите его и загрузите другой
  • Если существует только один, загрузите его
  • Если оба повреждены, сообщите пользователю, что их состояние сохранения было безвозвратно (как вы, вероятно, уже должны)

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

Кроме того, вы должны сохранять данные сразу после ключевых событий (например, покупок в приложении) в дополнение к интервальному сохранению, чтобы минимизировать окно уязвимости, когда сбой / уничтожение может привести к потере этого ключевого события. Это также защита от игровых эксплойтов (например, принудительное уничтожение приложения после потери жизни, поскольку вы знаете, что восстановите игровое состояние до того, как потеряли жизнь, и попытаетесь снова). Конечно, если вы делаете это, вы должны защитить интервальное сохранение с помощью логики, которая гласит: «Если сохранение уже выполняется, не пытайтесь начать сохранение снова».

MrCranky
источник
Спасибо, г-н Крэнки, это частичное решение моей проблемы. Как я могу сохранить данные последнего сеанса, т.е. непосредственно перед уничтожением приложения / игры? Например, он сделал покупку в приложении и убил приложение.
Фейсал Имран
12
Вам даже не нужно два файла. Записать в B, если и только если запись прошла успешно, удалить A и переименовать B в A. java.nio.file.Files.move(Path source, Path target, CopyOption... options)можно переименовать и перезаписать как элементарную операцию.
Полигном,
6
@Polygnome: обратите внимание, что в случае сбоев питания это не всегда дает ожидаемые гарантии, если только вы не вызовете sync()файл B, прежде чем переместить его в файл A (даже в этом случае нет полных гарантий, но sync()это довольно хорошо).
Дитрих Эпп
1
@FaisalImran Сохраняйте данные сразу после важной операции, например, после IAP. Если операция была прервана из-за выключения телефона, я думаю, что в любом случае это никогда не отнимет у человека деньги. Но на всякий случай вы можете захотеть сохранить данные своего аккаунта при покупке на какой-либо платформе, чтобы попытаться что-то купить. Затем вы можете сравнить свои доходы с этими данными и посмотреть, действительно ли этот человек что-то купил. А затем откройте для него эту функцию через онлайн-сервис, который вы используете для сохранения всех данных. Если вы используете локальные сохранения для IAP, то это еще хуже, чем эта проблема.
Candid Moon _Max_
1
Наконец, хотя это может решить не все ваши проблемы, я бы сказал, что ваша проблема не полностью решаема. Вы не можете поддерживать как «пользователь может убить мое приложение в любое время», так и «данные не должны быть потеряны никогда», потому что всегда будет окно после события, но до его сохранения в хранилище, где пользователь может убить приложение и потерять данные.
MrCranky
3

Android убивает приложения только в фоновом режиме. Нужно ли обновлять данные игры, когда приложение находится в фоновом режиме, или вы можете прекратить обновление данных, пока приложение не вернется на передний план? Возможно, вы сможете отложить обновления даже в многопользовательской игре, если вы сможете получить историю событий или даже просто текущее состояние, когда приложение вернется на передний план. Это то, на что вам, вероятно, стоит обратить внимание в любом случае ради эффективности процессора, сети и батареи.

Если пользователь убивает ваше приложение, работающие службы должны получить вызов onTaskRemoved. Я не знаю, что произойдет, если вы попытаетесь сделать много обработки в этом методе. Я ожидаю, что если он не вернется в течение определенного времени, Android будет kill -9вашим приложением.

Кевин Крумвиде
источник
Приложение не нуждается в обновлении в фоновом режиме, но приложение должно сохранить свои данные, т.е. файл json, перед тем как перейти в фоновый режим или уничтожить.
Фейсал Имран
вы правы, метод onTaskRemoved будет вызываться, но время, необходимое для сохранения файла, больше, или мы можем сказать, что время вычислений велико. Когда пользователь убивает приложение, этот метод будет остановлен.
Фейсал Имран