rcp(插件开发)Eclipse Jobs and Background Processing

1. Prerequisites for this tutorial

This tutorial assumes what you have basic understanding of development for the Eclipse platform. Please see Eclipse 4 RCP Tutorial or Eclipse Plug-in Tutorial if you need any basic information.

2. Eclipse Background Processing

2.1. User Interface Thread

By default Eclipse uses one Thread to run all the code instructions. This Thread also creates the event loop for the user interface (UI) and is the only Thread that is allowed to interact with the UI.

This Thread is called the UI thread or main thread. It is created by the org.eclipse.swt.widgets.Display class.

If another Thread, which is not the UI thread, tries to update the UI, the SWT framework will raise a SWTException exception.

org.eclipse.swt.SWTException: Invalid thread access 

All events in the user interface will be executed one after another. If you perform a long running operation in the UI thread, the user interface will not respond to any user interaction.

Therefore it is important not to block this thread, e.g. with network or file access otherwise the user interface will appear frozen.

All long running operations should be performed in a separate Thread.

The Eclipse framework provides ways for a Thread to synchronize itself with the user interface. It also provides the Eclipse Jobs Framework which allows you to run operations in the background using the Eclipse platform.

2.2. Eclipse 4 UISynchronize

The org.eclipse.e4.ui.di plug-in contains the UISynchronize class. An instance of this class is in the Eclipse 4 context and can be injected into an Eclipse 4 application via dependency injection.

UISynchronize also provides the syncExec() and asyncExec() methods to synchronize with the main thread.

Using UISynchronize is the preferred method rather than using the Display class, as this allows a consistent programming model using dependency injection.

2.3. Eclipse Jobs API

The Eclipse Jobs API provides support for running background processes. It allows you to provide feedback about the progress of the Job to the user via a progress indicator.

The important parts of the Job API are:

  • IJobManager - Schedules the jobs

  • Job - The individual task to perform

  • IProgressMonitor - Interface to communicate information about the status of your Job.

You create a Job via the following:

Job job = new Job("My Job") {
  @Override
  protected IStatus run(IProgressMonitor monitor) {
    // Do something long running
    //... 
            
    // If you want to update the UI
    Display.getDefault().asyncExec(new Runnable() {
      @Override
      public void run() {
        // Do something in the user interface
        // e.g. set a text field
      }
    });
    return Status.OK_STATUS;
  }
};

// Start the Job
job.schedule(); 

If you want to update the UI from a Job you still need to use the Display.syncExec() or asyncExec() methods.

2.4. Priorities of Jobs

You can set the Job priority via the job.setPriority() method using the constants defined in the Job class, e.g. Job.SHORT, Job.LONG, Job.BUILD and Job.DECORATE.

The job scheduler will use these priorities to determine in which order the Jobs are scheduled. For example Jobs with the priority Job.SHORT are scheduled before jobs with Job.LONG. Jobs with the priority Job.DECORATE are scheduled after all other jobs are finished. Check the JavaDoc of the Job class for details.

2.5. Blocking the UI and providing feedback

Sometimes you simply want to give the user the feedback that something is running without using Threads.

The easiest way to provide feedback is to change the cursor via the BusyIndicator.showWhile() method call.

// Show a busy indicator while the runnable is executed
BusyIndicator.showWhile(display, runnable); 

If this code is executed, the cursor will change to a busy indicator until the Runnable is done.

A more advanced approach is to use a ProgressMonitorDialog together with an IRunnableWithProgress.

3. Using syncExec() and asyncExec()

In Eclipse 3.x API based projects you cannot use dependency injection to get the UISynchronize instance injected.

In this case you can use the Display object which provides the syncExec() and asyncExec() methods to update the user interface from another Thread.

// Update the user interface asynchronously
Display.getDefault().asyncExec(new Runnable() {
  public void run() {
    // ... do any work that updates the screen ...
  }
});

// Update the user interface synchronously

Display.getDefault().syncExec(new Runnable() {
  public void run() {
    // Do any work that updates the screen ...
    // Remember to check if the widget
    // Still exists
    // Might happen if the Part was closed
  }
}); 

4. Reporting Progress

4.1. IProgressMonitor

The IProgressMonitor object can be used to report progress. Use the beginTask() method to define the total units of work and the worked() method to report the additional units of work.

Job job = new Job("My Job") {
 @Override
 protected IStatus run(IProgressMonitor monitor) {
  // Set total number of work units
  monitor.beginTask("Doing something time consuming here", 100);
  for (int i = 0; i < 5; i++) {
   try {
     // Sleep a second
     TimeUnit.SECONDS.sleep(1)
     
     monitor.subTask("I'm doing something here " + i);
     
     // Report that 20 units are done
     monitor.worked(20);
    } catch (InterruptedException e1) {
     e1.printStackTrace();
     return Status.CANCEL_STATUS;
    }
   }
  System.out.println("Called save");
  return Status.OK_STATUS;
  }
 };
job.schedule(); 

4.2. Reporting Progress in Eclipse 4

In Eclipse 4 you can report progress by implementing the IProgressMonitor interface.

You can for example add a ToolItem to a Toolbar of your Window Trim in your application model. This ToolItem can implement the IProgressMonitor interface.

This is demonstrated in the following example.

package com.example.e4.rcp.todo.ui.composites;

import javax.annotation.PostConstruct;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ProgressBar;

public class MyToolItem implements IProgressMonitor {
  private ProgressBar progressBar;

  @PostConstruct
  public void createControls(Composite parent) {
    System.out.println("ToolItem");
    progressBar = new ProgressBar(parent, SWT.SMOOTH);
    progressBar.setBounds(100, 10, 200, 20);
  }

  @Override
  public void worked(final int work) {
    Display.getDefault().asyncExec(new Runnable() {
      @Override
      public void run() {
        System.out.println("Worked");
        progressBar.setSelection(progressBar.getSelection() + work);
      }
    });
  }

  @Override
  public void subTask(String name) {

  }

  @Override
  public void setTaskName(String name) {

  }

  @Override
  public void setCanceled(boolean value) {

  }

  @Override
  public boolean isCanceled() {
    return false;
  }

  @Override
  public void internalWorked(double work) {
  }

  @Override
  public void done() {
    System.out.println("Done");

  }

  @Override
  public void beginTask(String name, int totalWork) {
    System.out.println("Starting");
  }
} 

This new element can be accessed via the model service and used as an IProgressMonitor for the job.

// EModelService injected as service
// Mapplication injected as application

Job job = new Job("My Job") {
  // As before
};

// Setting the progress monitor
IJobManager manager = job.getJobManager();

// ToolItem has the ID "statusbar" in the model
MToolControl element = (MToolControl) service.find("statusbar",
  application);

Object widget = element.getObject();
final IProgressMonitor p = (IProgressMonitor) widget;
ProgressProvider provider = new ProgressProvider() {
  @Override
  public IProgressMonitor createMonitor(Job job) {
    return p;
  }
};

manager.setProgressProvider(provider);
job.schedule(); 

A more advanced implementation could for example implement a Progress Monitoring OSGi Service and report progress to the user interface via the EventAdmin service.

5. Reporting Progress in Eclipse 3.x

To activate progress reporting in the status line in Eclipse 3.x you have to activate progress reporting in preWindowOpen() method of the WorkbenchWindowAdvisor.

6. Tutorial: Using Eclipse Jobs

Create a new Eclipse plug-in project "de.vogella.jobs.first" with a View and a Button included in this View.

Create the following MySelectionAdapter class.

package de.vogella.jobs.first.parts;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class MySelectionAdapter extends SelectionAdapter {
  private final Shell shell;

  public MySelectionAdapter(Shell shell) {
    this.shell = shell;
  }

  @Override
  public void widgetSelected(SelectionEvent e) {
    Job job = new Job("First Job") {
      @Override
      protected IStatus run(IProgressMonitor monitor) {
        doLongThing();
        syncWithUi();
        // Use this to open a Shell in the UI thread
        return Status.OK_STATUS;
      }

    };
    job.setUser(true);
    job.schedule();
  }

  private void doLongThing() {
    for (int i = 0; i < 10; i++) {
      try {
        // We simulate a long running operation here
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("Doing something");
    }
  }

  private void syncWithUi() {
    Display.getDefault().asyncExec(new Runnable() {
      public void run() {
        MessageDialog.openInformation(shell, "Your Popup ",
            "Your job has finished.");
      }
    });

  }
} 

Add an instance of MySelectionAdapter as SelectionListener to your Button.

Button button = new Button(parent, SWT.PUSH);
button.addSelectionListener(new MySelectionAdapter(shell)); 

To access the Shell in Eclipse 3.x you can use the getSite().getShell() method call. In Eclipse 4 you declare a field and let Eclipse inject the Shell.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值