Eclipse RCP SubMonitor使用方法

By | 2018年8月18日

将IProgressMonitor分成多SubMonitor可以控制每个子任务的progress。

1. 构造方法newChild

有两个重载的方法,一个参数的会调用两个参数的,SUPPRESS_BEGINTASK,表示在child monitor里调用beginTask(String, int)不会修改proress的title。

public SubMonitor newChild(int totalWork) {
    return newChild(totalWork, SUPPRESS_BEGINTASK);
}

public SubMonitor newChild(int totalWork, int suppressFlags);

2. 典型的使用

//Example 1: Typical usage of newChild
void myMethod(IProgressMonitor parent) {
    SubMonitor progress = SubMonitor.convert(parent, 100); 
    doSomething(progress.newChild(50));
    doSomethingElse(progress.newChild(50));
}

3. child可以不执行worked和done

创建children可以是progress更加平滑,child monitor可以不用调用worked方法和done方法。创建新child时,会使前一个child自动完成,即使前一个child啥也没做

//Example 2: Demonstrates the function of active children. Creating children
// is sufficient to smoothly report progress, even if worked(...) and done()
// are never called.
void myMethod(IProgressMonitor parent) {
    SubMonitor progress = SubMonitor.convert(parent, 100);
    
    for (int i = 0; i < 100; i++) {
        // Creating the next child monitor will clean up the previous one,
        // causing progress to be reported smoothly even if we don't do anything
        // with the monitors we create
      progress.newChild(1);
    }
}

4. child不能并行,只能一个一个执行

错误例子:因为只有一个SubMointor是active的,其它的不会report任何progress。

void wrongMethod(IProgressMonitor parent) {
  SubMonitor progress = SubMonitor.convert(parent, 100);
  callMethod(progress.newChild(50), computeValue(progress.newChild(50)));
}

正确做法:将方法分解,保证每次只有一个SubMonitor在被使用。

void rightMethod(IProgressMonitor parent) {
  SubMonitor progress = SubMonitor.convert(parent, 100);
  Object someValue = computeValue(progress.newChild(50));
  callMethod(progress.newChild(50), someValue);
} 

5. 一个完整的例子

参数SubMonitor.SUPPRESS_NONE使得生成的child可以修改progress title和sub title,否则得调用setTaskName(String)修改progress title。生成的child也可以设置工作量,调用worked方法和done方法逐步影响整体的progress。

SubMonitor subMonitor = SubMonitor.convert(monitor, "Start main work...", 100);
subMonitor.subTask("Sub task description...");
Thread.sleep(2000);

SubMonitor newChild1 = subMonitor.newChild(50, SubMonitor.SUPPRESS_NONE);
newChild1.beginTask("Retrieveing data 1...", 100);
newChild1.subTask("Sub task data 1...");
//Do something...
Thread.sleep(2000);

SubMonitor newChild2 = subMonitor.newChild(1, SubMonitor.SUPPRESS_NONE);
newChild2.beginTask("Retrieveing data 2...", 100);
newChild2.subTask("Sub task data 2...");
//Do something...
Thread.sleep(2000);

6. 最佳实践

  • SubMonitor.convert(IProgressMonitor, String, int):设置Title和work count。
  • Child->subTask(String)仅需设置sub title,无需worked和done。
  • 使用split代替newChild,无需check cancelled,否则newChild时应当判断是否cancelled。

注:

SubMonitor->split(…)是Eclipse 4.6加入的,用法和newChild完全一样,唯一的区别是:当用户点击cancel时,split方法会抛出OperationCanceledException。

private void runProgress(IRunnableWithProgress runnableWithProgress) {
  try {
    PlatformUI.getWorkbench().getProgressService().run(true, true, runnableWithProgress);
  } catch (InvocationTargetException | InterruptedException e) {
    LoggerFactory.getLogger(View.class).info(e::getMessage);
  }
}

runProgress(monitor -> {
  try {
    SubMonitor subMonitor = SubMonitor.convert(monitor, "Start main work...", 100);
    Thread.sleep(2000);

    SubMonitor newChild1 = subMonitor.split(50);
    newChild1.subTask("Retrieveing data 1...");
    //Do something...
    Thread.sleep(2000);

    SubMonitor newChild2 = subMonitor.split(50);
    newChild2.subTask("Retrieveing data 2...");
    //Do something...
    Thread.sleep(2000);
  } catch (OperationCanceledException e) {
    // Progress is cancelled.
    LoggerFactory.getLogger(View.class).info(e::getMessage);
  }
});