当用户关闭工作台时就会触发工作区保存处理,而在其它时间平台会定期触发工作区保存处理。插件可以参与工作区保存处理,因此,每当保存工作区的余下持久数据时,就会将关键的插件数据保存到磁盘中。
还可以使用工作区保存处理来跟踪在激活插件之间所发生的更改。
要参与工作区保存,必须将保存参与者添加到工作区中。这通常是在插件启动方法中完成的。还可以从中读取在上次关闭插件时可能保存的任何状态。
让我们看一下将演示保存进程的简单插件。
package com.example.saveparticipant; import org.eclipse.core.runtime.*; import org.eclipse.core.resources.*; import java.io.File; import java.util.*; public class MyPlugin extends Plugin { private static MyPlugin plugin; public MyPlugin(IPluginDescriptor descriptor) { super(descriptor); plugin = this; } public static MyPlugin getDefault() { return plugin; } protected void readStateFrom(File target) { } public void startup() throws CoreException { super.startup(); ISaveParticipant saveParticipant = new MyWorkspaceSaveParticipant(); ISavedState lastState = ResourcesPlugin.getWorkspace().addSaveParticipant(this, saveParticipant); if (lastState == null) return; IPath location = lastState.lookup(new Path("save")); if (location == null) return; // the plugin instance should read any important state from the file. File f = getStateLocation().append(location).toFile(); readStateFrom(f); } protected void writeImportantState(File target) { } }
ISaveParticipant 定义用于工作区保存参与者的协议。此接口的实现可以提供保存进程的不同阶段的行为。让我们看一下各个阶段,以及类 WorkspaceSaveParticipant 如何实现其中的每个步骤。
public void prepareToSave(ISaveContext context) throws CoreException { }
public void saving(ISaveContext context) throws CoreException { switch (context.getKind()) { case ISaveContext.FULL_SAVE: MyPlugin myPluginInstance = MyPlugin.getDefault(); // save the plug-in state int saveNumber = context.getSaveNumber(); String saveFileName = "save-" + Integer.toString(saveNumber); File f = myPluginInstance.getStateLocation().append(saveFileName).toFile(); // if we fail to write, an exception is thrown and we do not update the path myPluginInstance.writeImportantState(f); context.map(new Path("save"), new Path(saveFileName)); context.needSaveNumber(); break; case ISaveContext.PROJECT_SAVE: // get the project related to this save operation IProject project = context.getProject(); // save its information, if necessary break; case ISaveContext.SNAPSHOT: // This operation needs to be really fast because // snapshots can be requested frequently by the // workspace. break; } }
ISaveContext 描述有关保存操作的信息。有三种类型的保存操作:FULL_SAVE、SNAPSHOT 和 PROJECT_SAVE。保存参与者应根据他们已经接收到的保存事件的类型小心地执行相应的处理。例如,快照事件可能会相当频繁地发生,并且会允许插件保存它们的关键状态。花大量时间来保存状态(即使发生崩溃也可以重新计算)将降低平台的运行速度。
保存号码可用来创建使用序号(save-1、save-2 等)命名的数据保存文件。 将每个保存文件映射至独立于保存号码的逻辑文件名(保存)。将插件数据写入相应的文件,并可在以后不知道上次成功的保存操作的特定保存号码的情况下检索插件数据。再次回忆我们在插件的启动代码中看到的此技巧:
IPath location = lastState.lookup(new Path("save"));在保存了数据并映射了文件名之后, 调用 needSaveNumber 以指示我们已经积极参与了工作区保存, 并且想要为保存活动指定号码。可以按如上所述使用保存号码来创建数据文件。
public void doneSaving(ISaveContext context) { MyPlugin myPluginInstance = MyPlugin.getDefault(); // delete the old saved state since it is not necessary anymore int previousSaveNumber = context.getPreviousSaveNumber(); String oldFileName = "save-" + Integer.toString(previousSaveNumber); File f = myPluginInstance.getStateLocation().append(oldFileName).toFile(); f.delete(); }
在此处清理来自先前的保存操作的保存信息。使用 getPreviousSaveNumber 来获取在先前的保存操作(不是我们刚完成的保存操作)中指定的保存号。使用此号码来构造需要删除的文件的名称。注意,不要使用保存状态的逻辑文件映射,原因是我们已经映射了当前的保存文件号码。
public void rollback(ISaveContext context) { MyPlugin myPluginInstance = MyPlugin.getDefault(); // since the save operation has failed, delete the saved state we have just written int saveNumber = context.getSaveNumber(); String saveFileName = "save-" + Integer.toString(saveNumber); File f = myPluginInstance.getStateLocation().append(saveFileName).toFile(); f.delete(); }
在此处删除刚保存的状态。注意,使用当前保存号码来构造刚保存的文件的文件名。不必担心,我们已经将此文件名映射到 ISaveContext 中。保存操作失败时,平台将废弃该上下文。
如果插件在保存有效期中的任何时间抛出了异常, 则将从当前保存操作中除去它,并且将不会获得余下的任何有效期方法。例如,如果您在保存方法中失败了, 则您将不会接收到回滚或完成保存消息。
在将保存参与者添加到工作区中时,它将返回 ISavedState 对象, 该对象描述在插件的上次保存操作期间插件保存的内容 (或者如果插件先前未保存任何状态,则保存的内容为 null)。可以使用此对象来访问前一保存文件中的信息(通过使用保存号码和文件映射), 或者处理在激活插件之间发生的更改。
如果使用文件映射来保存根据保存号码来逻辑命名的文件, 则可以使用此相同映射来检索上一个所知的保存状态中的数据。
ISaveParticipant saveParticipant = new MyWorkspaceSaveParticipant(); ISavedState lastState = ResourcesPlugin.getWorkspace().addSaveParticipant(myPluginInstance, saveParticipant); if (lastState != null) { String saveFileName = lastState.lookup(new Path("save")).toString(); File f = myPluginInstance.getStateLocation().append(saveFileName).toFile(); // the plugin instance should read any important state from the file. myPluginInstance.readStateFrom(f); }
在激活插件之前,回忆一下工作区中可能会发生的任何数目的资源更改事件。如果想要知道自让您的插件无效后发生了哪些更改, 则可以使用保存机制来执行此操作,即使您不需要保存其它任何数据。
保存参与者必须请求平台自己保存资源变化。这是作为保存操作的一部分来完成的。
public void saving(ISaveContext context) throws CoreException { // no state to be saved by the plug-in, but request a // resource delta to be used on next activation. context.needDelta(); }
在插件启动期间,可以访问前一保存的状态,并且将为自上次保存后发生的所有更改创建更改事件。
ISaveParticipant saveParticipant = new MyWorkspaceSaveParticipant(); ISavedState lastState = ResourcesPlugin.getWorkspace().addSaveParticipant(myPluginInstance, saveParticipant); if (lastState != null) { lastState.processResourceChangeEvents(new MyResourceChangeReporter()); }
所提供的类必须实现 IResourceChangeListener,正如跟踪资源更改中描述的那样。自上次保存后发生的更改是作为 POST_AUTO_BUILD 资源更改事件的一部分来报告的。
注意:存储在 ISavedState 中的更改事件中不会报告标记更改。必须假定自保存了上一个状态后已经更改了任何或所有标记。