我们刚刚了解过如何在可运行程序中对资源更改进行批处理(对资源更改进行批处理)。我们再来看看另一方面。如果想要跟踪插件运行时发生的所有工作空间更改又该怎么办?可向工作空间注册 IResourceChangeListener。将通过 IResourceChangeEvent 对象(用来描述更改)来向侦听器通知更改。
首先必须向工作空间注册资源更改侦听器。
IResourceChangeListener listener = new MyResourceChangeReporter();
ResourcesPlugin.getWorkspace().addResourceChangeListener(
listener, IResourceChangeEvent.POST_CHANGE);
在执行了对工作空间资源的修改之后,将通知侦听器。修改资源的资源 API 方法将触发这些事件,作为描述行为的一部分。资源 API 方法的方法注释显式声明它是否触发了资源更改事件。例如,以下是 IFile.setContents() 注释中所包含的内容:
This method changes resources; these changes will be reported in a subsequent
resource change event, including an indication that this file's content have
been changed.
通常,创建、删除或更改资源的方法将触发这些事件。读取但不写入资源的方法通常不会触发这些事件。
资源更改事件描述在工作空间中已经发生的更改(或更改集合)的细节。事件中包含资源变化,它描述更改的净效应。例如,如果您在一次批处理中添加了资源,稍后又删除了该资源,则该资源将不会出现在变化中。
资源变化构造为起源于工作空间根目录的树。资源变化树描述下列类型的更改:
要遍历资源变化树,可以实现 IResourceDeltaVisitor 接口,或者使用 IResource.getAffectedChildren 来显式遍历树。资源变化访问器实现这样一种 visit 方法,当资源变化枚举树中的每个更改时,就要调用此方法。
注意:资源变化中不会标识对资源会话属性或者资源持久属性所作的更改。
每当对工作空间进行更改(或一批更改)时,就会发送资源更改事件。另外,对于某些特定工作空间操作也会发送资源更改事件。下表总结了资源更改事件的类型以及是何时报告它们的。
事件类型 |
描述 |
---|---|
PRE_CLOSE |
通知侦听器项目即将关闭。此事件可以用来在关闭项目之前从该项目的内存表示法(例如,会话属性)中抽取和保存所需的信息。(关闭项目时,将丢弃其内存表示法)。在处理此事件期间,会锁定工作空间(不能更新任何资源)。事件中包含正关闭的项目。 |
PRE_DELETE |
通知侦听器即将删除项目。此事件可以用来执行清理操作,例如,从插件的目录中除去与项目相关的任何保存状态。在进行此事件期间,会锁定工作空间(不更新任何资源)。事件中包含正删除的项目。 |
PRE_AUTOBUILD |
在发生任何自动构建之前通知侦听器。当平台检测到需要进行自动构建时,就会广播此事件,而不管实际上是否启用了自动构建。在进行此事件期间,工作空间未锁定(可以更新资源)。事件包含用来描述自上次报告 POST_CHANGE 事件后发生的更改的资源变化。 |
POST_AUTOBUILD |
在发生任何自动构建后通知侦听器。在平台执行自动构建后广播此事件,而不管实际上是否启用了自动构建。在进行此事件期间,工作空间未锁定(可以更新资源)。事件包含用来描述自上次报告 POST_CHANGE 事件后发生的更改的资源变化。 |
POST_CHANGE |
描述自上次报告 POST_CHANGE 事件后对工作空间进行的一组更改。在单独使用资源更改 API 之后或者在一批工作空间更改中触发。还会在任何 PRE_AUTOBUILD 或 POST_AUTOBUILD 通知完成后触发。事件包含用来描述自上一个 POST_CHANGE 事件后的净更改的资源变化。在进行此事件期间,会锁定工作空间(不能更新任何资源)。 |
以下示例实现基于控制台的资源更改侦听器。为特定类型的事件注册了资源更改侦听器,而将关于这些事件的信息打印至控制台:
IResourceChangeListener listener = new MyResourceChangeReporter();
ResourcesPlugin.getWorkspace().addResourceChangeListener(listener,
IResourceChangeEvent.PRE_CLOSE
| IResourceChangeEvent.PRE_DELETE
| IResourceChangeEvent.PRE_AUTO_BUILD
| IResourceChangeEvent.POST_AUTO_BUILD
| IResourceChangeEvent.POST_CHANGE);
侦听器检查每种事件类型,并报告关于被更改的资源的信息,以及发生的更改的种类。尽管此示例用来显示处理所有类型的资源事件的通用侦听器,但是,典型的侦听器只为一种类型的事件注册。
POST_CHANGE 的实现使用另一个类,该类可以用来访问资源变化中的更改。
import org.eclipse.resources.*;
import org.eclipse.runtime.*;
public class MyResourceChangeReporter implements IResourceChangeListener {
public void resourceChanged(IResourceChangeEvent event) {
IResource res = event.getResource();
switch (event.getType()) {
case IResourceChangeEvent.PRE_CLOSE:
System.out.print("Project ");
System.out.print(res.getFullPath());
System.out.println(" is about to close.");
break;
case IResourceChangeEvent.PRE_DELETE:
System.out.print("Project ");
System.out.print(res.getFullPath());
System.out.println(" is about to be deleted.");
break;
case IResourceChangeEvent.POST_CHANGE:
System.out.println("Resources have changed.");
event.getDelta().accept(new DeltaPrinter());
break;
case IResourceChangeEvent.PRE_AUTO_BUILD:
System.out.println("Auto build about to run.");
event.getDelta().accept(new DeltaPrinter());
break;
case IResourceChangeEvent.POST_AUTO_BUILD:
System.out.println("Auto build complete.");
event.getDelta().accept(new DeltaPrinter());
break;
}
}
}
DeltaPrinter 类实现 IResourceDeltaVisitor 接口,以查询资源变化。对资源变化中的每个资源更改都会调用 visit() 方法。访问者使用返回值来指示是否应访问子资源的变化。
class DeltaPrinter implements IResourceDeltaVisitor {
public boolean visit(IResourceDelta delta) {
IResource res = delta.getResource();
switch (delta.getKind()) {
case IResourceDelta.ADDED:
System.out.print("Resource ");
System.out.print(res.getFullPath());
System.out.println(" was added.");
break;
case IResourceDelta.REMOVED"
System.out.print("Resource ");
System.out.print(res.getFullPath());
System.out.println(" was removed.");
break;
case IResourceDelta.CHANGED:
System.out.print("Resource ");
System.out.print(res.getFullPath());
System.out.println(" has changed.");
break;
}
return true; // visit the children
}
}
可以从提供的资源变化中获取进一步的信息。以下代码段显示可以如何实现 IResourceDelta.CHANGED 事例,以进一步描述资源更改。
...
case IResourceDelta.CHANGED:
System.out.print("Resource ");
System.out.print(delta.getFullPath());
System.out.println(" has changed.");
int flags = delta.getFlags();
if ((flags & IResourceDelta.CONTENT) != 0) {
System.out.println("--> Content Change");
}
if ((flags & IResourceDelta.REPLACED) != 0) {
System.out.println("--> Content Replaced");
}
if ((flags & IResourceDelta.MARKERS) != 0) {
System.out.println("--> Marker Change");
IMarkerDelta[] markers = delta.getMarkerDeltas();
// if interested in markers, check these deltas
}
break;
...
有关资源变化、访问器和标记变化的完整描述,参阅 IResourceDelta、IResourceDeltaVisitor 和 IMarkerDelta 的 API 规范。
注意:如果激活了插件,资源更改侦听器对于跟踪对资源的更改是很有用的。如果插件在它的启动代码中注册了资源更改侦听器,则可能在激活插件之前就已经触发了许多资源更改事件。对于插件接收到的第一个资源更改事件中所包含的资源变化,它将不包含自上次激活插件后所作的所有更改。如果需要跟踪在激活插件之间所作的更改,则应当使用为工作空间保存所提供的支持。在工作空间保存参与中对此作了描述。
注意:某些资源更改事件是在处理后台线程时触发的。资源更改侦听器应该是线程安全的。有关用户界面的线程安全的讨论,请参阅线程技术问题。