实现强大的持久性撤消/重做功能

时间:2010-10-27 23:42:15

标签: android design-patterns mobile undo

我正在编写一个位图编辑器,我使用命令模式来表示将转换文档的操作。我将目前执行的所有命令保存在列表中,为了实现撤消,我将文档恢复到其初始状态,然后重放除最后一个命令之外的所有命令。

我希望我的undo / redo系统具有以下功能:当用户关闭编辑器并返回时,文档包括可用的undo和redo命令应该恢复到状态就在用户离开的时候。

我正在为Android实现此功能,您可以在从内存中清除应用程序之前通知您的应用程序,例如,用户接到电话。另外,我的一些命令是例如用户绘制的所有x,y co-ord的列表,这些可能需要一些时间才能保存到磁盘。

我目前的想法如下:

  1. 执行新操作时,命令对象将添加到列表S,以获取需要保存到磁盘的命令。
  2. 使用后台线程,它将继续从列表S获取命令并将它们保存到磁盘。使用的文件名的后缀将按顺序编号。例如,如果用户填满屏幕然后绘制了2个圆圈,则命令文件可能被称为FillCommand1.cmd,DrawCircleCommand2.cmd,DrawCircleCommand3.cmd。
  3. 我们会定期保存一个“检查点”命令,其目的是存储完整的文档状态,这样即使其中一个.cmd文件已损坏,我们也可以恢复该文档的最新版本。
  4. 当用户退出应用程序时,后台线程会尝试完成保存所有命令(但可能会被杀死)。
  5. 在启动时,我们会查找最新的.cmd文件,该文件代表我们可以成功加载的检查点。我们可以在此之后加载所有.cmd文件(即某些文件可能已损坏)进入重做命令列表,我们可以在加载的第一个检查点和我们可以加载的最旧检查点之间加载的所有.cmd文件进入撤消列表
  6. 我希望撤消限制大约是20或30个命令,所以我需要额外的逻辑来丢弃命令,删除.cmd文件,我不得不担心多线程行为。这个系统看起来相当复杂,需要进行大量测试才能确保它不会出错。

    Java或Android中有什么比这更容易吗?我在哪里重新发明轮子?也许数据库会更好?

2 个答案:

答案 0 :(得分:0)

嗯,您的代码本质上可能是必要的,其中应用程序的状态由用户的操作修改到位。这可能是快速和直接的。撤消基本上是时间旅行,如果您通过修改状态来破坏旧状态,则必须存储配方以反向重新计算它,或者存储可以重新计算它的历史记录。

就像你说的那样,你可以存储动作和初始状态并向前播放(在用户选择的历史记录的新点停止),但这意味着撤消一个动作可能导致n个动作重放。一种方法是将已保存的状态副本存储在历史列表中,以便您可以立即跳转到给定状态。为了避免使用太多的RAM /存储,如果您的系统很聪明,它可以检测历史记录中最近的(非空)保存状态并重新计算这几个stpes(假设您拥有所需的所有操作 - 这假设操作是小和状态是大(r))直到达到正确的状态。通过这种方式,您可以开始消除旧的已保存状态(删除或设置为空)(根据成本函数将状态下降到与状态回溯的时间成反比线性),对最近的过去快速撤消,以及内存/对古代历史有效的储存。我用这种方法取得了成功。

答案 1 :(得分:0)

不要回复原始版本然后执行所有操作,而应考虑使命令可逆。这样,如果您决定增加撤消历史记录的大小,则在撤消时不会引入滞后的可能性。或者,正如Jared Updike所说,您的应用程序可能会受益于近期和未来的缓存渲染结果。

我认为您使用基于文件系统的解决方案过度复杂化了。如果要维护当前工作文档的整个历史记录的备份,则应该在附加模式下保持未缓冲的日志打开,并将操作记录到它。日志应该与正在编辑的应用程序和文件的特定实例相关联,因此您不必担心另一个线程踩到您的脚趾。从该日志加载应该与从普通保存文件加载非常相似。只要在遇到撤消操作时丢弃最后读取的操作。