1.1.1 报告进度
长时间运行的作业(它们的持续时间比其它作业更长)应该向被传递至作业的 run 方法的 IProgressMonitor 报告进度。工作台进度视图将显示所有进度消息和对此监视器提供的已完成的工作单元。
所提供的进度监视器还应该用来检查来自进度视图的取消请求。当用户(或使用作业 API 的插件)尝试取消作业时,IProgressMonitor方法 isCanceled() 将返回true。作业应该不断检查作业的取消状态,并通过在一旦检测到取消就尽快退出 run 方法来响应取消。以下 run 方法将报告进度并响应作业取消:
public IStatus run(IProgressMonitor monitor) {
final int ticks = 6000;
monitor.beginTask("Doing some work", ticks);
try {
for (int i = 0; i < ticks; i++) {
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
monitor.subTask("Processing tick #" + i);
//... do some work ...
monitor.worked(1);
}
} finally {
monitor.done();
}
return Status.OK_STATUS;
}
beginTask方法用来命名相应进度视图中的任务和确定要完成的工作总量,以便视图可以估计进度。当工作完成时,subTask消息将作为进度树中的子代出现。进度视图将根据 worked 调用中报告的工作量来计算和显示完成百分比。
1.1.2 进度监视器和用户界面
正如您可以看到的那样,IProgressMonitor类是在考虑了相应的用户界面支持的情况下设计的。平台的用户界面插件提供支持,以便工作台可以显示正在运行的作业的进度。可以在具有这种概念的情况下设置作业,以便能够控制如何显示作业。
要详细查看可用于显示作业进度的 API,请参阅工作台并发性支持。
系统作业
如果作业是不想对用户显示的低级实现详细信息,则怎么办?可以将作业标记为系统作业。除了相应的用户界面支持将不设置进度视图或显示与运行作业相关联的任何其它用户界面内容之外,系统作业与任何其它作业是一样的。如果作业不是由用户直接启动也不是可以由用户配置的定期任务,则该作业应该是系统作业。用于设置系统作业的协议很简单:
class TrivialJob extends Job {
public TrivialJob() {
super("Trivial Job");
setSystem(true);
}
...
}
在调度作业之前,必须调用 setSystem。如果对当前正在等待、正在休眠或正在运行的作业尝试执行此调用,就会产生异常。
用户作业
如果作业是由用户启动的长时间运行的操作,则应该将作业标记为用户作业。用户作业将显示在模态进度对话框中,该对话框提供了用于将它移至后台的按钮。工作台定义了一个用户首选项来控制这些对话框是否是模态的。通过将作业定义为用户作业,进度反馈将自动符合进度视图的用户首选项。用于设置用户作业的协议是相似的:
class TrivialJob extends Job {
public TrivialJob() {
super("Trivial Job");
setUser(true);
}
...
}
在调度作业之前,还必须进行 setUser 调用。
1.1.3 进度组
进度组是可以用来影响作业在用户界面中的显示方式的另一种机制。当在用户界面中显示几个相关作业的聚集进度更合适时,可以创建用来表示一组相关作业的特殊 IProgressMonitor。此监视器是使用 IJobManager 协议来创建的。以下片段说明如何创建进度组并将它与作业相关联。
...
IJobManager jobMan = Platform.getJobManager();
myGroup = jobMan.createProgressGroup();
job.setProgressGroup(myGroup, 600); // specify the units of work the job needs to show.
job.schedule()
...
组设施允许插件在需要时将任务分为多个作业,但是向用户报告这些作业时一定要当作单个任务来报告。进度组监视器将处理计算相对于该组中的所有作业的完成百分比的详细信息。
在调度作业之前,必须将它放置到进度组中。在作业运行完之后,它就会失去对进度组的引用。如果要再次调度该作业,则在调度它之前必须将它再次设置到组中。
1.1.4 作业调度
到目前为止,我们的示例演示了简单的作业创建、调度和进度报告。作业调度机制实际上比我们到目前为止所显示的功能更强大。可以通过使用优先级、延迟和定制调度条件来对调度作业的方式进行更细粒度的控制。
作业优先级
作业优先级可以用来确定一个作业相对于系统中的其它作业的重要性。设置作业的优先级将不影响已经在运行的作业,但是它会影响正在等待的作业相对于其它作业的调度顺序。作业的优先级可以是预定义的几个优先级常量的其中一个:
- INTERACTIVE 作业通常优先于其它作业。它们应该是短时间运行或者较少使用处理器的作业,因此,它们不会阻碍其它 INTERACTIVE 作业运行。
- SHORT 作业通常会在一秒钟内完成,但是也可能会时间稍微长一点。它们在后台运行,并且优先于除了 INTERACTIVE 作业之外的所有作业。
- LONG 作业表示更长时间运行的后台作业。它们只有在 INTERACTIVE 作业和 SHORT 作业已经运行之后才运行。
- BUILD 作业表示与构建任务相关联的作业。它们比 LONG 作业的优先级更低。BUILD 作业只有在所有 LONG 作业完成之后才运行。
- DECORATE 作业在系统中的优先级最低。它们用于那些提供可以帮助补充用户界面的信息但是用户通常不会等待的任务。
作业的缺省优先级是 LONG。以下片段将创建我们先前使用的普通作业,但是会将优先级设置为 DECORATE 以指示它是最低级别的优先级:
TrivialJob job = new TrivialJob();
job.setPriority(Job.DECORATE);
job.schedule();
具有延迟的调度
这是用于控制如何调度作业以使用调度延迟的另一种技术。可以在调度作业时指定调度延迟。在调度作业之前,作业将延迟指定的毫秒数。
TrivialJob job = new TrivialJob();
job.schedule(1000); // wait one second before scheduling
重新调度作业
调度已经在等待或者休眠的作业没有任何效果。但是,调度已经在运行的作业将导致在完成该作业之后重新调度它。对于重复的作业(例如,后台轮询循环)来说,这是一种很方便的机制。如果作业在运行时被多次重新调度,则它将只按照最近提供的延迟重新调度一次。以下片段定义一个作业,当它完成当前迭代之后,它将重新调度它自己运行 10 秒种。
class RepetitiveTrivialJob extends Job {
public RepetitiveTrivialJob() {
super("Repetitive Trivial Job");
}
public IStatus run(IProgressMonitor monitor) {
System.out.println("Running the job.");
// reschedule after 10 seconds
schedule(10000);
return Status.OK_STATUS;
}
}
定制调度条件
Job类中的附加协议允许作业就在调度或运行它之前检查先决条件。以下示例清楚地说明了这一点:
class JobWithPreconditions extends Job {
...
public boolean shouldSchedule() {
return super.shouldSchedule() && checkJobPreconditions();
}
public boolean shouldRun() {
return super.shouldRun() && checkJobPreconditions();
}
...
}
shouldSchedule方法是刚好在作业管理器将作业置于队列中之前调用的。如果不满足调度的基本先决条件,这允许作业取消它自己。作业应该返回false,不合适调度它。类似地,shouldRun方法是刚好在作业管理器运行作业之前调用的。此时,必须检查在作业运行之前必须满足的任何其它条件。