Back to top

vogella training Training Books

Eclipse Jobs and Background Processing

Lars Vogel

Version 2.5

02.08.2012

Revision History
Revision 0.1 12.07.2009 Lars
Vogel
Created
Revision 0.2 - 2.5 15.07.2009 - 02.08.2012 Lars
Vogel
bug fixes and enhancements

Background processing in Eclipse Plug-ins

This tutorial describes the concept of the main user interface thread in SWT and how to synchronize other threads with this thread.

It also explains the usage of the Jobs API in Eclipse plug-in projects for performing asynchronous tasks.

This tutorial can be used for Eclipse 3.x and Eclipse 4.x based plug-ins.


Table of Contents

1. Prerequisites for this tutorial
2. Eclipse Background Processing
2.1. User Interface Thread
2.2. Eclipse 4 UISynchronize
2.3. Eclipse Jobs API
2.4. Priorities of Jobs
2.5. Blocking the UI and providing feedback
3. Using syncExec() and asyncExec()
4. Asynchronous processing and the event bus
5. Reporting Progress
5.1. IProgressMonitor
5.2. Reporting Progress in Eclipse 4
6. Reporting Progress in Eclipse 3.x
7. Tutorial: Using Eclipse Jobs
8. Thank you
9. Questions and Discussion
10. Links and Literature
10.1. Eclipse Jobs resources
10.2. Source Code
10.3. vogella Resources

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 runs the event loop for the user interface (UI) and is the only Thread that is allowed to interact with the UI. It is called the UI thread or main thread.

If another 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 are 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 provides the syncExec() and asyncExec() methods to synchronize with the main thread.

Using UISynchronize is the preferred method rather than using the Display.getDefault().asyncExec() or Display.getDefault().ayncExec() methods, 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:

// Get UISynchronize injected as field
@Inject UISynchronize sync;

// More code

Job job = new Job("My Job") {
  @Override
  protected IStatus run(IProgressMonitor monitor) {
    // Do something long running
    //... 
            
    // If you want to update the UI
    sync.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 synchronize yourself with the user interface.

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. Asynchronous processing and the event bus

Eclipse 4 provides the IEventBrooker which allows to send events. Your threads can use the IEventBrooker to send event data. Every listener will be automatically called and if you use the UIEventTopic annotation, this method is be automatically called in the user interface thread.

// Get the IEventBrooker via DI
@Inject
IEventBroker brooker;

// Somewhere in you code you do something 
// performance intensive

button.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        Runnable runnable = new Runnable() {
          public void run() {
            for (int i = 0; i < 10; i++) {
              try {
                Thread.sleep(500);
              } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
              }
              // Send out an event to update
              // the UI
              brooker.send("update", i);
            }
          }
        };
        new Thread(runnable).start();
      }
    });



// More code
// ....


// Get notified and sync automatically
// with the UI thread

@Inject @Optional
public void  getEvent(@UIEventTopic("update") int i) {
  // text1 is a SWT Text field
  text1.setText(String.valueOf(i));
  System.out.println(i);
} 

5. Reporting Progress

5.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(); 

5.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().syncExec(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) {
    sync.syncExec(new Runnable() {
      @Override
      public void run() {
        progressBar.setMaximum(totalWork);
        progressBar.setToolTipText(name);
      }
    });
    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.

6. 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.

7. 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.

@Inject Shell shell 

Start your application or the Eclipse workbench with your plug-in and press the Button. A dialog is opened.

8. Thank you

Please help me to support this article:

Flattr this

9. Questions and Discussion

Before posting questions, please see the vogella FAQ. If you have questions or find an error in this article please use the www.vogella.com Google Group. I have created a short list how to create good questions which might also help you.

10. Links and Literature

10.2. Source Code

Source Code of Examples

10.3. vogella Resources

vogella Training Android and Eclipse Training from the vogella team

Android Tutorial Introduction to Android Programming

GWT Tutorial Program in Java and compile to JavaScript and HTML

Eclipse RCP Tutorial Create native applications in Java

JUnit Tutorial Test your application

Git Tutorial Put everything you have under distributed version control system