blob: 7647eba9cdfec7f0e7dc63af7f4237b43a6c8b3d [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<document id="background-tasks">
<properties>
<title>Background Tasks</title>
</properties>
<body>
<p>
Often, an application will need to perform a long-running task (such as loading a large
file or executing a computationally-intensive operation). Executing such code on the UI
thread will cause the application to stop responding to repaints while the operation is
running, giving the appearance that it is "hung".
</p>
<p>
Pivot includes the <tt>org.apache.pivot.util.concurrent.Task</tt> class to help resolve
this problem. A <tt>Task</tt> represents an instance of an operation that may be
performed by a background thread. It is an abstract class, whose <tt>execute()</tt>
method is overridden by an application to perform the actual task, optionally returning
a value when it is complete.
</p>
<p>
Though callers can invoke the <tt>execute()</tt> method directly, this would have the
same effect as simply executing the code inline: the UI will appear to hang until
<tt>execute()</tt> returns. However, <tt>Task</tt> provides an additional version of
the <tt>execute()</tt> method that takes an instance of
<tt>org.apache.pivot.util.concurrent.TaskListener</tt>. When this version of the method
is invoked, the <tt>Task</tt> class creates (or obtains) an instance of a background
thread and calls the abstract version of the method on this thread instead of the UI
thread. When the abstract <tt>execute()</tt> returns, the task notifies the caller by
invoking one of the methods defined by the listener interface:
</p>
<p>
<tt>public void taskExecuted(Task&lt;V&gt; task);</tt><br/>
<tt>public void executeFailed(Task&lt;V&gt; task);</tt><br/>
</p>
<p>
If the <tt>execute()</tt> method returns successfully, the <tt>taskExecuted()</tt>
method is called, and the result of the operation can be obtained by calling the
<tt>getResult()</tt> method of the task object. However, if the <tt>execute()</tt> call
fails (by throwing an exception), the <tt>executeFailed()</tt> listener method is
called, and the exception that was thrown can be obtained via the <tt>getFault()</tt>
method of the task object.
</p>
<p>
The following example demonstrates the behavior of an application when a task is
executed synchronously vs. asynchronously. It uses a simple "sleep task" to simulate an
operation that runs for five seconds:
</p>
<application class="org.apache.pivot.wtk.ScriptApplication"
width="240" height="240">
<libraries>
<library>core</library>
<library>wtk</library>
<library>wtk-terra</library>
<library>tutorials</library>
</libraries>
<startup-properties>
<src>/org/apache/pivot/tutorials/backgroundtasks/background_tasks.bxml</src>
</startup-properties>
</application>
<p>
Pressing the "Execute Synchronously" button activates an activity indicator component
and calls the synchronous version of the <tt>execute()</tt> method of the sleep task.
Though the task runs and returns correctly, the activity indicator is never shown,
because the UI thread has been unable to respond to repaints while the task was
running. However, when the "Execute Asynchronously" button is pressed, the activity
indicator appears correctly and runs for five seconds until the task is complete.
</p>
<p>
The BXML for this example is shown below:
</p>
<source type="xml" location="org/apache/pivot/tutorials/backgroundtasks/background_tasks.bxml">
<![CDATA[
<backgroundtasks:BackgroundTasks title="Background Tasks" maximized="true"
xmlns:bxml="http://pivot.apache.org/bxml"
xmlns:backgroundtasks="org.apache.pivot.tutorials.backgroundtasks"
xmlns="org.apache.pivot.wtk">
<BoxPane orientation="vertical"
styles="{horizontalAlignment:'center', verticalAlignment:'center'}">
<Border styles="{padding:2}">
<ActivityIndicator bxml:id="activityIndicator"/>
</Border>
<PushButton bxml:id="executeSynchronousButton" buttonData="Execute Synchronously"/>
<PushButton bxml:id="executeAsynchronousButton" buttonData="Execute Asynchronously"/>
</BoxPane>
</backgroundtasks:BackgroundTasks>
]]>
</source>
<p>
The Java source for <tt>SleepTask</tt> is as follows. It simply sleeps for 5000
milliseconds and then returns a simulated result value:
</p>
<source type="java" location="org/apache/pivot/tutorials/backgroundtasks/SleepTask.java">
<![CDATA[
package org.apache.pivot.tutorials.backgroundtasks;
import org.apache.pivot.util.concurrent.Task;
import org.apache.pivot.util.concurrent.TaskExecutionException;
public class SleepTask extends Task<String> {
@Override
public String execute() throws TaskExecutionException {
// Simulate a long-running activity (5s)
try {
Thread.sleep(5000);
} catch (InterruptedException exception) {
throw new TaskExecutionException(exception);
}
// Return a simulated result value
return "Done sleeping!";
}
}
]]>
</source>
<p>
The Java source for the application is shown below:
</p>
<source type="java" location="org/apache/pivot/tutorials/backgroundtasks/BackgroundTasks.java">
<![CDATA[
package org.apache.pivot.tutorials.backgroundtasks;
import java.net.URL;
import org.apache.pivot.beans.Bindable;
import org.apache.pivot.collections.Map;
import org.apache.pivot.util.Resources;
import org.apache.pivot.util.concurrent.Task;
import org.apache.pivot.util.concurrent.TaskExecutionException;
import org.apache.pivot.util.concurrent.TaskListener;
import org.apache.pivot.wtk.ActivityIndicator;
import org.apache.pivot.wtk.Button;
import org.apache.pivot.wtk.ButtonPressListener;
import org.apache.pivot.wtk.PushButton;
import org.apache.pivot.wtk.TaskAdapter;
import org.apache.pivot.wtk.Window;
public class BackgroundTasks extends Window implements Bindable {
private ActivityIndicator activityIndicator = null;
private PushButton executeSynchronousButton = null;
private PushButton executeAsynchronousButton = null;
@Override
public void initialize(Map<String, Object> namespace, URL location, Resources resources) {
activityIndicator = (ActivityIndicator)namespace.get("activityIndicator");
executeSynchronousButton = (PushButton)namespace.get("executeSynchronousButton");
executeAsynchronousButton = (PushButton)namespace.get("executeAsynchronousButton");
executeSynchronousButton.getButtonPressListeners().add(new ButtonPressListener() {
@Override
public void buttonPressed(Button button) {
activityIndicator.setActive(true);
System.out.println("Starting synchronous task execution.");
SleepTask sleepTask = new SleepTask();
String result = null;
try {
result = sleepTask.execute();
} catch (TaskExecutionException exception) {
System.err.println(exception);
}
System.out.println("Synchronous task execution complete: \"" + result + "\"");
activityIndicator.setActive(false);
}
});
executeAsynchronousButton.getButtonPressListeners().add(new ButtonPressListener() {
@Override
public void buttonPressed(Button button) {
activityIndicator.setActive(true);
setEnabled(false);
System.out.println("Starting asynchronous task execution.");
SleepTask sleepTask = new SleepTask();
TaskListener<String> taskListener = new TaskListener<String>() {
@Override
public void taskExecuted(Task<String> task) {
activityIndicator.setActive(false);
setEnabled(true);
System.out.println("Synchronous task execution complete: \""
+ task.getResult() + "\"");
}
@Override
public void executeFailed(Task<String> task) {
activityIndicator.setActive(false);
setEnabled(true);
System.err.println(task.getFault());
}
};
sleepTask.execute(new TaskAdapter<String>(taskListener));
}
});
}
}
]]>
</source>
<p>
Note that the button press listener for the "Execute Asynchronously" button wraps the
actual task listener in an instance of <tt>org.apache.pivot.wtk.TaskAdapter</tt>. This
is because, like most UI toolkits, Pivot user interfaces are single-threaded: all UI
operations must occur on the same thread (which AWT calls the "event dispatch thread").
Wrapping the task listener in a <tt>TaskAdapter</tt> ensures that the result listener
will be called on the UI thread, rather than the background thread, which is what would
occur if the listener was not wrapped in the adapter.
</p>
</body>
</document>