1.1 内容类型
org.eclipse.core.runtime.content包为定义数据流的内容类型提供了支持。内容类型由 Eclipse 的与内容相关的几项功能(例如,自动编码确定、选择比较编辑器和菜单添加项)使用。IContentTypeManager管理的中央内容注册表允许插件定义内容类型,并指定了如何读取和描述内容的类。要添加内容类型,必须对内容框架具有基本的了解。
注意:在此讨论中,当谈到内容时,我们特地避免使用文件这个词。运行时内容引擎不会假定内容包含在文件系统的文件中。但是,它确实包括使内容类型与文件命名模式相关联的协议。实际上,这些文件名表示文件系统中的文件,但是内容系统的实现中的任何对象都不假定内容在文件系统中。文件编码和内容类型讨论由平台资源插件添加的面向文件的内容类型。
1.1.1 定义和描述内容平台定义一些基本内容类型(例如,纯文本和 XML数据流)。定义这些内容类型所采用的方式与定义由其它插件添加的内容类型的方式相同。我们将了解平台如何定义文本内容类型以便更好地了解内容类型框架。
插件通过为 org.eclipse.core.runtime.contentTypes扩展点添加扩展来定义内容类型。在此扩展中,插件为内容类型指定标识和名称,还指定IContentDescriber(它可以读取输入流并提供对内容的描述)。以下片段是运行时插件的文本内容类型的添加项:
<extension point="org.eclipse.core.runtime.contentTypes">
<content-type id="text" name="%textContentTypeName">
priority="high"
file-extensions="txt">
<describer class="org.eclipse.core.internal.content.TextContentDescriber"/>
</content-type>
...
TextContentDescriber负责读取输入流并快速确定提供的内容是否是有效的文本样本。每当平台尝试确定特定数据流的内容时,就会调用describe(inputStream, description)方法。IContentDescriber负责快速确定内容是否表示其内容类型的有效样本并返回用来指示内容与其类型是否相匹配的常量。如果内容确实与其类型相匹配,则描述器还应该使用有关数据的信息来填充所提供的IContentDescription。
IContentDescription将特定于内容的属性存储在键/值对中。这些属性是特定于特定内容类型的。平台指定字符集的属性以及文本文件的字节顺序,但是还可以定义其它内容。
1.1.2 了解内容类型
IContentTypeManager定义内容注册表的协议。客户机可以使用此类来测试内容流或了解系统中的其它内容类型。
- findContentType... 方法允许客户机获取流的内容类型和文件名模式。
- getAllContentTypes 允许客户机获取在平台中定义的所有内容类型。
- getContentType 允许客户机按内容类型的唯一标识获取内容类型。
- getDescriptionFor... 方法允许客户机获取给定流的内容描述和文件名模式。
内容类型由 IContentType表示。此类表示知道如何读取数据流并解释特定于内容类型的信息的唯一内容类型。实质上,内容类型是分层的。例如,XML数据的内容类型被认为是文本内容类型的子代。
<content-type id="xml" name="%xmlContentTypeName"
base-type="text"
priority="high"
file-extensions="xml"
default-charset="UTF-8">
<describer class="org.eclipse.core.internal.content.XMLContentDescriber"/>
</content-type>
这允许新的内容类型利用更一般的内容类型的属性或行为。
字符集
平台文本内容类型不为文本内容定义字符集。文本内容类型的子代可以自由指定不同的缺省字符集(当适当时),正如 XML内容类型那样。XML流的缺省字符集为 UTF-8,这意味着如果 XML文件在它的内容中没有明确声明编码,则认为它的编码为 UTF-8。
内容类型冲突
两个独立的插件可能为同一种类的内容添加内容类型。在这种情况下,平台将只为内容选择一个内容描述器。选择的描述器是使用可以在 contentTypes扩展中指定的priority 属性来确定的。如果两个插件为具有相同优先级的相同内容添加内容类型,则将选择哪个内容描述器是不确定的。但是,一旦选择了内容描述器,对“丢失的”内容描述器的所有注册表引用都将作为选择的那个内容注册表的别名。
1.2 并发性基础结构
复杂系统的其中一个主要问题是在执行任务时保持响应状态。在可扩充的系统中,当不是用来一起运行的组件却共享相同的资源时,这种问题更为突出。org.eclipse.core.runtime.jobs包通过提供用于调度、执行和管理同时运行的操作的基础结构来解决了这种问题。此基础结构基于使用作业来表示可以异步运行的工作单元。
作业
Job类表示与其它作业同时运行的异步工作单元。要执行任务,插件将创建作业,然后调度它。一旦调度了作业,就会将它添加至由平台管理的作业队列。平台使用后台调度线程来管理所有暂挂的作业。当运行的作业完成时,就会从队列中除去它,平台将确定接下来运行哪一个作业。当作业变为活动状态时,平台就会对它调用run()方法。通过下面这个简单的示例很好地说明了作业:
class TrivialJob extends Job {
public TrivialJob() {
super("Trivial Job");
}
public IStatus run(IProgressMonitor monitor) {
System.out.println("This is a job");
return Status.OK_STATUS;
}
}
在以下片段中创建并调度了作业:
TrivialJob job = new TrivialJob();
System.out.println("About to schedule a job");
job.schedule();
System.out.println("Finished scheduling a job");
此程序的输出与计时有关。即,对于创建了作业并且调度了作业的线程来说,没有方法可确定何时将执行作业的run方法。输出将为下面两种情况的其中一种:
About to schedule a job
This is a job
Finished scheduling a job
或者:
About to schedule a job
Finished scheduling a job
This is a job
如果想要确定在继续下一步之前作业是否已完成,则可以使用join()方法。此方法将阻塞调用者,直到作业已完成或者调用线程被中断为止。让我们以更确定的方式改写上面的片段:
TrivialJob job = new TrivialJob();
System.out.println("About to schedule a job");
job.schedule();
job.join();
if (job.getResult().isOk())
System.out.println("Job completed with success");
else
System.out.println("Job did not complete successfully");
假定 join()调用未被中断,则会保证此方法返回以下结果:
About to schedule a job
This is a job
Job completed with success
当然,在调度作业之后立即连接它通常不是很有用,原因是这样做不能获得并发性。在这种情况下,还可能直接在调用线程中从作业的 run方法来完成该工作。稍后让我们查看有关在何处使用连接更有意义的一些示例。
最后一个片段还使用了作业结果。结果是从作业的run()方法返回的 IStatus对象。可以使用此结果来从作业的 run 方法传递回任何必需对象。还可以使用结果来指示故障(通过返回严重性为IStatus.ERROR的 IStatus)或者取消(IStatus.CANCEL)。
常见作业操作
我们已经了解了如何调度作业并等待它完成,但是,还可以对作业执行许多其它有意义的操作。如果调度了一个作业,然后又决定不再需要它,则可以使用cancel()方法来停止该作业。如果取消作业时该作业尚未开始运行,则会立即放弃而不会运行它。。另一方面,如果作业已经开始运行,则将由作业来决定它是否想要响应取消。当您尝试取消作业时,使用join()方法来等待取消作业早晚会有用的。以下是用于取消作业并在继续下一步之前一直等到作业完成为止的习惯用语:
if (!job.cancel())
job.join();
如果取消没有立即生效,则 cancel() 将返回 false,调用者将使用join()来等待成功取消作业。
比取消操作程度稍轻的方法是 sleep() 方法。对于此方法,如果作业尚未开始运行,则此方法将导致该作业被无限期挂起。平台将仍然记住该作业,而wakeUp()调用将把该作业添加至等待队列,最终还是将执行该作业。
作业状态
作业在它的生存期内将经历几种状态。不但可以通过诸如cancel()和 sleep())的 API来处理它,而且在平台运行和完成该作业时,它的状态也会更改。作业可以经历下列状态:
- 等待指示已调度作业运行,但是它尚未运行。
- 正在运行指示作业正在运行。
- 正在休眠指示作业由于休眠请求或者调度它在经过一定延迟之后才运行而正在休眠。
- 无指示作业未在等待、运行或休眠。当创建了作业但是尚未调度它时,作业就会处于此状态。在作业完成运行或者已被取消之后,它也将处于此状态。
如果作业当前处于等待状态,则只能将它置于休眠状态。唤醒正在休眠的作业将使它重新回到等待状态。取消作业时将使它返回到无状态。
如果插件需要知道特定作业的状态,则它可以注册作业更改侦听器,当该作业在其生命周期内移动时就会通知该侦听器。这对于显示作业的进度或报告是很有用的。
作业更改侦听器
Job方法addJobChangeListener可以用来注册特定作业上的侦听器。IJobChangeListener定义用于响应作业中的状态更改的协议:
- aboutToRun 是在作业将要运行时发送的。
- awake 是在先前处于休眠状态的作业现在正在等待运行时发送的。
- done 是在作业完成执行时发送的。
- running 是在作业开始运行时发送的。
- scheduled 是在作业已被调度并正在作业队列中等待时发送的。
- sleeping 是在将正在等待的作业置于休眠状态时发送的。
在所有这些情况下,都会为侦听器提供 IJobChangeEvent,它指定正在经历状态更改的作业以及作业完成时的状态(如果作业已执行)。
注意:作业还定义了 getState() 方法,该方法用于获取作业的当前状态(相对状态)。但是,此结果并不始终都是可靠的,原因是作业在不同线程中运行,并且可能会根据调用返回的时间再次更改状态。作业更改侦听器是用于发现作业中的状态更改的建议机制。
作业管理器
IJobManager定义用于处理系统中的所有作业的协议。显示进度或使用作业基础结构的插件可以使用IJobManager来执行一些任务(例如,暂挂系统中的所有作业、找出哪个作业正在运行或者接收关于特定作业的进度反馈)。可以使用Platform API来获得平台的作业管理器:
IJobManager jobMan = Platform.getJobManager();
对系统中所有作业的状态都感兴趣的插件可以对作业管理器注册作业更改侦听器,而不是对各个作业都注册侦听器。
作业系列
有时,插件将一组相关作业作为单个单元来处理会更容易。这可以通过使用作业系列来完成。作业通过覆盖belongsTo方法来声明它属于某个系列:
public static final String MY_FAMILY = "myJobFamily";
...
class FamilyJob extends Job {
...
public boolean belongsTo(Object family) {
return family == MY_FAMILY;
}
}
IJobManager协议可以用来取消、连接、休眠或查找一个作业系列中的所有作业:
IJobManager jobMan = Platform.getJobManager();
jobMan.cancel(MY_FAMILY);
jobMan.join(MY_FAMILY, null);
由于作业系列是使用任意对象表示的,因此,可以将感兴趣的状态存储在作业系列本身中,并且作业可以根据需要动态地构建系列对象。一定要使用完全是唯一的系列对象,以避免与其它插件创建的系列意外地交互。
通过系列还可以很方便地找到多组作业。IJobManager.find(Object family)方法可以用来找到任何给定时间所有正在运行、正在等待和正在休眠的作业的实例。
88

被折叠的 条评论
为什么被折叠?



