工作区保存参与

当用户关闭工作台时就会触发工作区保存处理, 而在其他时间平台会定期触发工作区保存处理。插件可以参与工作区保存处理,因此,每当保存工作区的余下持久数据时, 就会将关键的插件数据保存到磁盘中。

还可以使用工作区保存处理来跟踪在激活插件之间所发生的更改。

实现保存参与者

要参与工作区保存,必须将保存参与者添加到工作区中。这通常是在插件的启动方法中完成的。让我们看一下将演示保存进程的简单插件。

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;
        // 插件实例将读取文件中的任何重要状态。
        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();
                // 保存插件状态
                int saveNumber = context.getSaveNumber();
                String saveFileName = "save-" + Integer.toString(saveNumber);
                File f = myPluginInstance.getStateLocation().append(saveFileName).toFile();
               
// 如果无法写入,则会抛出异常,并且不会更新路径
                myPluginInstance.writeImportantState(f);
                context.map(new Path("save"), new Path(saveFileName));
                context.needSaveNumber();
                break;
            case ISaveContext.PROJECT_SAVE:
                // 获取有关此保存操作的项目
                IProject project = context.getProject();
                // 必要时保存它的信息
                break;
            case ISaveContext.SNAPSHOT:
                // 此操作应该非常快,
                // 因为工作区会频繁请求
                // 进行快照。
                break;
        }
    }

ISaveContext 描述有关保存操作的信息。有三种类型的保存操作:FULL_SAVESNAPSHOTPROJECT_SAVE。保存参与者应根据他们已经接收到的保存事件的类型小心地执行相应的处理。例如,快照事件可能会相当频繁地发生,并且会允许插件保存它们的关键状态。花大量时间来保存状态(即使发生崩溃也可以重新计算)将降低平台的运行速度。

保存号码可用来创建使用序列号(save-1save-2 等)命名的数据保存文件。  将每个保存文件映射至独立于保存号码的逻辑文件名(保存)。将插件数据写入相应的文件,并可在以后不知道上次成功的保存操作的特定保存号码的情况下检索插件数据。再次回忆我们在插件的启动代码中看到的此技巧:

IPath location = lastState.lookup(new Path("save"));

在保存了数据并映射了文件名之后, 调用 needSaveNumber 以指示我们已经积极参与了工作区保存, 并且想要为保存活动指定号码。可以按如上所述使用保存号码来创建数据文件。

    public void doneSaving(ISaveContext context) {
        MyPlugin myPluginInstance = MyPlugin.getDefault();

        // 删除旧的保存状态,原因是不再需要它了
        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();

        // 因为保存操作失败了,所以要删除我们刚刚写入的保存状态
        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();
    // 插件实例将读取文件中的任何重要状态。
    myPluginInstance.readStateFrom(f);
}

处理激活之间的资源变化

在激活插件之前,回忆一下工作区中可能会发生的任何数目的资源更改事件。如果想要知道自让您的插件无效后发生了哪些更改, 则可以使用保存机制来执行此操作,即使您不需要保存其他任何数据。

保存参与者必须请求平台自己保存资源变化。这是作为保存操作的一部分来完成的。

public void saving(ISaveContext context) throws CoreException {
    // 插件不需要保存任何状态,
    // 但会在下一次激活时请求资源变化。
    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 中的更改事件中不会报告标记更改。必须假定自保存了上一个状态后已经更改了任何或所有标记。