对这个问题的一般性质感到抱歉,但我不想从任何假设开始,因为害怕错过大局
我有一个文档编辑类型的应用程序(音乐符号)并且想要实现撤消和重做。所有相关数据都保存在此
static ArrayList <TTEvt> mEvList;
在我的 windows/MFC 应用程序中,我只是序列化数据结构并将其放在堆栈上。它使用大量内存,但简单且万无一失。
所以我想知道在android中保存和恢复我的ArrayList的最糟糕的方法是什么?
谢谢
对这个问题的一般性质感到抱歉,但我不想从任何假设开始,因为害怕错过大局
我有一个文档编辑类型的应用程序(音乐符号)并且想要实现撤消和重做。所有相关数据都保存在此
static ArrayList <TTEvt> mEvList;
在我的 windows/MFC 应用程序中,我只是序列化数据结构并将其放在堆栈上。它使用大量内存,但简单且万无一失。
所以我想知道在android中保存和恢复我的ArrayList的最糟糕的方法是什么?
谢谢
您可以使用 Memento 设计模式:这里有一个使用此模式的示例撤消/重做http://www.youtube.com/watch?v=jOnxYT8Iaoo&list=PLF206E906175C7E07&index=25
所以这是工作的多级撤消/重做代码。它工作正常,但它很慢。我取出了 gzip,这有点帮助,但它基本上使我的 UI 无法使用。但至少它有效,我可以尝试从这里优化。
static LinkedList<byte[]> undoStack = new LinkedList<byte[]>();
static LinkedList<byte[]> redoStack = new LinkedList<byte[]>();
public static void addUndoCheckpoint() {
long start = System.currentTimeMillis();
byte[] byteBuf = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// GZIPOutputStream gzipOut = new GZIPOutputStream(baos);
ObjectOutputStream objectOut = new ObjectOutputStream(baos);
for (TTEvt ev : Doc.mEvList)
objectOut.writeObject(ev);
objectOut.close();
byteBuf = baos.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
undoStack.push(byteBuf);
if (undoStack.size() > 10)
undoStack.removeLast(); // limit size
redoStack.clear();
long end = System.currentTimeMillis();
Log.d("MainView", "addUndoCheckpoint time=" + (end - start) + "mS");
}
public static void doUndo() {
if (undoStack.size() == 0)
return;
// push current state onto redo stack first
byte[] byteBuf = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// GZIPOutputStream gzipOut = new GZIPOutputStream(baos);
ObjectOutputStream objectOut = new ObjectOutputStream(baos);
for (TTEvt ev : Doc.mEvList)
objectOut.writeObject(ev);
objectOut.close();
byteBuf = baos.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
redoStack.push(byteBuf); // push current state onto redo stack
if (redoStack.size() > 10)
redoStack.removeLast();
// now undo
mEvList.clear();
byteBuf = undoStack.pop();
ObjectInputStream objectIn = null;
try {
ByteArrayInputStream bais = new ByteArrayInputStream(byteBuf);
// GZIPInputStream gzipIn;
// gzipIn = new GZIPInputStream(bais);
objectIn = new ObjectInputStream(bais);
while (true) {
TTEvt ev = (TTEvt) objectIn.readObject();
if (ev == null)
break;
Doc.mEvList.add(ev);
}
objectIn.close();
} catch (IOException e) {
// this is the normal exit
if (objectIn != null) {
try {
objectIn.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
// e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
MainView.forceTotalRedraw();
}
public static void doRedo() {
if (redoStack.size() == 0)
return;
// push current state onto undo stack first so we can undo the redo
byte[] byteBuf = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// GZIPOutputStream gzipOut = new GZIPOutputStream(baos);
ObjectOutputStream objectOut = new ObjectOutputStream(baos);
for (TTEvt ev : Doc.mEvList)
objectOut.writeObject(ev);
objectOut.close();
byteBuf = baos.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
undoStack.push(byteBuf); // push current state onto redo stack
if (undoStack.size() > 10)
undoStack.removeLast();
// now redo
mEvList.clear();
byteBuf = redoStack.pop();
ObjectInputStream objectIn = null;
try {
ByteArrayInputStream bais = new ByteArrayInputStream(byteBuf);
// GZIPInputStream gzipIn;
// gzipIn = new GZIPInputStream(bais);
objectIn = new ObjectInputStream(bais);
while (true) {
TTEvt ev = (TTEvt) objectIn.readObject();
if (ev == null)
break;
Doc.mEvList.add(ev);
}
objectIn.close();
} catch (IOException e) {
// this is the normal exit
if (objectIn != null) {
try {
objectIn.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
// e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
MainView.forceTotalRedraw();
}
}
请注意,您应该在更改 ArrayList 之前调用 addUndoCheckpoint。我目前正在开发一个您可以在更改 ArrayList 后调用的版本。这样做的好处是您可以在后台执行 addUndoCheckpoint 而不会减慢 UI。
处理此问题的一种方法不是从数组中删除项目,而只是将它们标记为已删除并刷新视图以使它们消失。所以重做只会取消标记它们。根据应用程序的逻辑,在某些操作中,应从数组中永久删除已删除的项目。
也可以有一个单独的数组,其中撤消项所在的位置,因为该数组只保留对对象的引用,因此它不会占用太多内存并且它可以是有限的大小。
但是,如果撤消/重做需要在设备关闭后继续存在,那么将每个项目的值保存到文件或数据库是唯一的选择。
仅供参考,我想出了一些选择。
最简单的方法是使用 toArray 将 ArrayList 写入一个简单的对象数组,如下所示:
static TTEvt evArray[];
public static void addUndoCheckpoint() {
long start = System.currentTimeMillis();
evArray = Doc.mEvList.toArray(new TTEvt[Doc.mEvList.size()]);
long end = System.currentTimeMillis();
Log.d("MainView", "addUndoCheckpoint time="+(end-start)+"mS");
}
public static void doUndo() {
Doc.mEvList.clear();
for(TTEvt ev : evArray)
Doc.mEvList.add(ev);
forceTotalRedraw();
}
更新:我刚刚发现上面的代码并没有真正起作用,因为 toArray 只对对象的引用数组做了一个 new,而不是它们本身的对象。所以我还需要克隆所有对象,这显然是很多内存,可能还有时间。也许答案是下面的慢速选项,使用序列化但在同步线程中执行,这样它就不会减慢 UI 速度。
使用 gzip 压缩来节省空间的更复杂的方法
static byte[] undoBuf;
public static void addUndoCheckpoint() {
long start = System.currentTimeMillis();
//evArray = Doc.mEvList.toArray(new TTEvt[Doc.mEvList.size()]);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzipOut = new GZIPOutputStream(baos);
ObjectOutputStream objectOut = new ObjectOutputStream(gzipOut);
for(TTEvt ev: Doc.mEvList)
objectOut.writeObject(ev);
objectOut.close();
undoBuf = baos.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
Log.d("MainView", "addUndoCheckpoint time="+(end-start)+"mS");
}
public static void doUndo() {
Doc.mEvList.clear();
//for(TTEvt ev : evArray)
// Doc.mEvList.add(ev);
try {
ByteArrayInputStream bais = new ByteArrayInputStream(undoBuf);
GZIPInputStream gzipIn;
gzipIn = new GZIPInputStream(bais);
ObjectInputStream objectIn = new ObjectInputStream(gzipIn);
while(objectIn.available()>0) {
TTEvt ev = (TTEvt) objectIn.readObject();
Doc.mEvList.add(ev);
}
objectIn.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
forceTotalRedraw();
}
问题在于它的速度很慢,对于大约 5000 个条目的数据结构来说几乎是 1 秒
目前这只是一个 1 级撤消,目前实现了一个堆栈以保持多个级别,并寻找更快的内存压缩