by Lars Vogel

Follow me on twitter

Lars Vogel on Google+

Eclipse Jobs and Background Processing

Lars Vogel

Version 2.3

28.01.2012

Revision History
Revision 0.1 12.07.2009 Lars
Vogel
Created
Revision 0.2 - 2.3 15.07.2009 - 28.01.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. UI Thread
2.2. Using syncExec() and asyncExec()
2.3. Eclipse Jobs API
2.4. Priorities of Jobs
2.5. Blocking the UI and providing feedback
3. Reporting Progress
3.1. IProgressMonitor
3.2. Reporting Progress in Eclipse 3.x
3.3. Reporting Progress in Eclipse 4
4. Tutorial: Using Eclipse Jobs
5. Thank you
6. Questions and Discussion
7. Links and Literature
7.1. Eclipse Jobs resources
7.2. Source Code
7.3. vogella Resources

1. Prerequisites for this tutorial

This article assumes what you have basic understanding of development for the Eclipse platform. Please see Eclipse RCP Tutorial or Eclipse Plugin Tutorial if you need any further information.

2. Eclipse Background Processing

2.1. UI Thread

If you don't interfere, Eclipse uses one Thread to run all the instructions in your coding. 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 an SWTException exception.

				
org.eclipse.swt.SWTException: Invalid thread access
			

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

Therefore it is important not to block this thread, e.g. with network or file access. Otherwise the UI 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 to run operations in the background using the Eclipse platform.

2.2. Using syncExec() and asyncExec()

If you need to update the user interface from another Thread, you can use the syncExec() and asyncExec() methods of the Display object.

				
// This will update the UI asynchronously, e.g. the calling thread will continue
Display.getDefault().asyncExec(new Runnable() {
	public void run() {
		// ... do any work that updates the screen ...
	}
});

// This will update the UI synchronously, e.g. the calling thread will wait
// until the work is done

Display.getDefault().syncExec(new Runnable() {
	public void run() {
		// ... do any work that updates the screen ...
	}
});
			

2.3. Eclipse Jobs API

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

The important parts of the Job API are:

  • JobManager - Schedules the jobs

  • Job - The individual task to perform

  • IProgressMonitor - UI for Job information

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 job.setPriority() method using the constants defined in the Job class, e.g. Job.SHORT, Job.LONG and Job.BUILD.

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.BUILD are scheduled after all other jobs are finished. Check the JavaDoc for the Job class for details.

2.5. Blocking the UI and providing feedback

Sometimes you simple 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 a IRunnableWithProgress.

3. Reporting Progress

3.1. IProgressMonitor

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

				
Job job = new Job("Mein 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 {
					Thread.sleep(1000);
					monitor.subTask("I'm doing something here " + i);
					// Report that 20 units are done
					monitor.worked(20);
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
			}
		System.out.println("Called save");
		return Status.OK_STATUS;
		}
	};
job.schedule();
			

3.2. Reporting Progress in Eclipse 3.x

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

While this was easy to activate, it only supported a infinite progress indicator.

3.3. Reporting Progress in Eclipse 4

Eclipse 4 does allow that you implement your own IProgressMonitor.

Eor example you can add an ToolItem to a Toolbar of your Window Trim in your application model which implements IProgressMonitor.

				
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 ToolItem 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 access via the model service and used as IProgressMonitor

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

Job job = new Job("Mein 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();
			

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

5. Thank you

Please help me to support this article:

Flattr this

6. 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.de Google Group. I have created a short list how to create good questions which might also help you.

7. Links and Literature

7.2. Source Code

Source Code of Examples

7.3. vogella Resources

Eclipse RCP Training (German) Eclipse RCP Training with Lars Vogel

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