I'm developing a desktop GUI app. I'm using Swing at the moment, but I want to be able to port it to JavaFX or Android sometime in the future.
Currently, I'm struggling with implementing a background task class which would be "UI agnostic": the goal is to be able to submit background tasks from application's core packages (not depending on a particular GUI) and observe the progress of tasks if there is a GUI (using GUI specific progress bars etc.).
Swing has it's SwingWorker<T, V>
(link), JavaFX has Task<V>
(link), Android has AsyncTask<Params, Progress, Result>
(link). They all serve the same purpose and in fact have similar APIs.
This interface provides the essential functionality for a background task to be useful, which is in fact found in all mentioned toolkits.
public interface BackgroundTask<Result, PartialResult> {
/**
* This is where the background computation belongs. Background thread.
* <p>
* Equivalent of {@link javax.swing.SwingWorker#doInBackground()}, {@link javafx.concurrent.Task#call()}
* and {@link android.os.AsyncTask#doInBackground()}.
*/
public Result computeResult();
/**
* Can be called from {@link #computeResult()} to update GUI with partial
* results. Background thread.
* <p>
* Equivalent of {@link javax.swing.SwingWorker#publish(Object[])}, and
* {@link android.os.AsyncTask#publishProgress(Progress)} (sort of). The equivalent in
* JavaFX is not just a simple method.
*/
public void partialUpdate(PartialResult... partials);
/**
* Called to process partial results. UI thread.
* <p>
* Equivalent of {@link javax.swing.SwingWorker#process(java.util.List)} and
* {@link android.os.AsyncTask#onProgressUpdate(Progress)}
*/
public void onPartialResult(List<PartialResult> partials);
}
My background tasks will implement this interface. I need some code to turn instances of BackgroundTask to a SwingWorker in Swing (Task in JavaFX and AsyncTask in Android later).
A straightforward implementation follows:
public class SwingBackgroundTask<Result, PartialResult> extends SwingWorker<Result, PartialResult> {
private final BackgroundTask<Result, PartialResult> task;
public SwingBackgroundTask(BackgroundTask<Result, PartialResult> task) {
this.task = task;
}
@Override
protected Result doInBackground() throws Exception {
return task.computeResult();
}
@Override
protected void process(List<PartialResult> chunks) {
task.onPartialResult(chunks);
}
}
However, it is not useful. Obviously, I cannot call SwingWorker's publish(partialResult)
from inside BackgroundTask's computeResult()
. Therefore my users will never see meaningful progress of background tasks. Sure I can use indeterminate progress bars but I don't like them.
How can I overcome this problem and make a generic background task?
Seeing as you're trying to decouple the process from a specific implementation, you will need to provide some kind of API which would allow you to provide feedback in some uniform manner.
Probably the simplest approach would be to provide the BackgroundTask
class with some kind of observer/listener, which interested parties could register with for notification and the BackgroundTask
could use to provide feedback.
This would allow BackgroundTask
to raise events about a given state change or other notification and the implementations would be able to handle in an implementation specific manner.