【问题标题】:Avoiding data loss due to interruption when saving files on android?在android上保存文件时避免因中断而丢失数据?
【发布时间】:2012-05-02 02:11:50
【问题描述】:

我想知道其他人在 Android 上保存文件时使用什么策略来避免数据丢失。我有几个游戏应用程序,基本上,它们可能会在用户暂停时保存游戏状态/保存游戏(onPause)。

这在 99.99% 的情况下都有效,但每隔一段时间我都会收到一个保存过程出错的存档游戏示例。通常,这是一个格式错误的 XML 文件,通常在某个任意点被截断。根据我收到的错误报告,我认为问题主要发生在用户在游戏过程中被电话或类似的东西打断,然后 Android 操作系统在应用程序完成保存之前将其杀死。保存文件的代码非常简单,所以我很难看出还有什么可能导致这个问题。

这是一个严重的问题,因为它通常会导致玩家的保存进度被破坏。

我想先写入一个空文件,然后再复制到“真实”文件,但我怀疑这只会增加问题,因为它总体上需要更多时间并且仍然有被中断的风险。

谁有安全的方式来做到这一点,Android 相对保证不会搞砸?


如此总结,目前建议的选项(据我了解):

  1. 为保存过程使用服务,假设这不太可能被操作系统杀死。
  2. 保存在临时文件中;验证保存后复制。
  3. 游戏状态的增量保存(我实际上已经将它用于玩家日志信息)。
  4. 2 和 3 的组合。
  5. 将保存移至另一个线程,因为问题可能出在 ANR 杀死 [DC 的 cmets 下面]。

我认为 SharedPreferences 不适用于这种结构化数据,目前,这两种方法似乎都不是理想的解决方案,所以我仍然愿意接受建议。


我还没有设法测试所有这些方法,因此我没有浪费赏金,而是将其分配给我认为最有可能解决问题的答案。不过,在接受答案之前,我仍计划检查各种选项。感谢所有好的建议。

【问题讨论】:

  • 保存到SharedPreference的数据是不是太多了?
  • 是的 - 大约 20K 的 XML 数据。这可能不是一个主要问题(没有尝试过),但我认为数据结构相当复杂。
  • 你是如何在onPause()中保存游戏状态的?您是否在主 (UI) 线程上执行 I/O?您将游戏状态保存在哪里(在 SD 卡或其他位置)?
  • 是的,它通常在 onPause 上完成,并且直接从函数(主线程)中完成。从来没有费心将它移动到线程中,因为保存时间从来都不是问题(据我所知),除了(大概)这个问题。
  • 我要补充:5。将保存移动到另一个线程。 到您的列表。你说它的发生率为 0.01%,这让我相信这是某些 Android 设备不支持并发文件系统访问的问题。如果您在任何应用程序上阻塞 UI 线程,Android 将在几秒钟后将其终止。如果你线程它,Android将没有理由杀死它,因为UI线程没有被阻塞。现在,如果可能,您应该/可能等待onFinish() 中的线程。但是在杀死应用的情况下,Android 甚至不保证会调用onFinish()

标签: android xml file


【解决方案1】:

对于服务来说,这听起来不错。

http://developer.android.com/reference/android/app/Service.html

【讨论】:

  • 据我了解,服务也可以被强制终止,并连接到启动它的进程。在这种情况下,不确定这会有多大帮助,因为问题似乎是由 Android 操作系统强制终止应用程序引起的。
  • 一个服务在后台运行,操作系统会反抗你的应用。也许你应该考虑这个建议......
  • 任何东西都可以被操作系统杀死,但是,我仍然相信你想要使用的服务。
  • 服务是一样的,不管你的应用是否还在运行,它都会在后台保存你的数据。来自安卓文档:Once started, a service can run in the background indefinitely, even if the component that started it is destroyed. Usually, a started service performs a single operation and does not return a result to the caller. For example, it might download or upload a file over the network. When the operation is done, the service should stop itself.
  • 是的,但这并没有太大帮助。一方面,如果内存不足,它仍然会被杀死。它能够重新启动并再次恢复并没有真正的帮助,因为这样做的全部目的是保存应用程序的状态(现在将丢失)。我能真正看到的唯一明显优势是它提高了托管进程的优先级,可以想象这可以让整个事情运行足够长的时间来完成工作。但如果这就是我们所需要的,那么肯定会有更好的方法来做到这一点。
【解决方案2】:

当我们从主线程对持久存储(内部存储器上的私有文件系统)进行 I/O(通常是写入)时,我们偶尔会遇到问题。通常这根本不需要太多时间,但偶尔会莫名其妙地花费很长时间(20 或 30 秒或更长时间)。似乎某些设备上的 Android 文件系统实现不支持并发访问(请参阅this,因此如果另一个进程正在使用文件系统,您的 I/O 可能会阻塞。如果您在主线程上执行 I/O,如果阻塞时间过长,操作系统可以/将会终止您的 Activity。这可能是发生在您身上的事情。

由于这个问题,我建议您将所有 I/O 移动到单独的线程(而不是主线程)。因此,例如,为了保存游戏状态,在 onPause() 中调用一个将游戏状态序列化为 ByteArrayOutputStream 的方法,然后您将其移交给单独的线程以最终写入文件系统。

【讨论】:

  • 这听起来很有趣;当然,我需要调查一下。
【解决方案3】:

我认为在这种情况下,每次用户执行任何操作时(不仅是在调用onPause() 时)将一小部分数据(不是整个游戏状态)保存到数据库中是最好的方法。但是,这可能需要对代码进行大量修改。

折衷方案是将游戏状态拆分为更小的部分或子状态(例如,round1round2、...、players 等)并在用户执行任何操作时再次将数据存储到适当的文件中,而不是等待onPause() 调用。这将显着降低丢失的概率,并且至少可以保证用户不会丢失整个游戏进度。此外,为了避免在保存过程中断时可能出现的应用程序状态不一致,您最好先将数据保存到临时文件,并且只有在成功的情况下才简单地重命名文件,而不是复制其内容(比如说,round1.xml.tmp 重命名为 round1.xml)。

【讨论】:

    【解决方案4】:

    首先,我会尝试查看应用程序是否真的在没有通知的情况下被杀死,因为我认为情况不应该如此。应用程序可能会因打电话之类的事情而停止,但我真的不认为 android 只是在没有任何通知的情况下杀死它。它可以决定结束它,但不能不保存状态等。

    也许只是保存本身发生了错误。

    一种不需要太多修改即可检查的方法是使用校验和或使用其他方法来验证已保存数据的完整性(大小、结束标记等)。你所要做的就是保存它,阅读它并检查它(记录任何错误)并重复它直到它正确。如果你保存大量数据可能不是那么好,但对于纯粹的游戏数据来说应该没问题。

    您还可以在应用程序启动和结束时设置一个状态,然后您会在下次启动时知道它上次是否正确结束以及是否需要采取额外措施。

    【讨论】:

    • 很难确定,因为我自己无法重现这种情况。但我收到的所有此类问题的确认报告通常都是用户被电话或类似情况打断的情况。
    • 我相当确定保存本身没有错误;在正常情况下,XML API 应该无法写入这种类型的截断文件。
    【解决方案5】:

    如果可能,请使用SharedPreferences 来存储您的游戏状态。只有在您致电 commit() 后,您的更改才会提交。您可以在onSaveInstanceState 回调中进行保存。阅读docs here.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-13
      • 1970-01-01
      • 2012-03-01
      • 1970-01-01
      • 2019-10-18
      • 1970-01-01
      相关资源
      最近更新 更多