| <?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<V> task);</tt><br/> |
| <tt>public void executeFailed(Task<V> 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> |