diff --git a/docs/html/training/multiple-threads/communicate-ui.jd b/docs/html/training/multiple-threads/communicate-ui.jd new file mode 100644 index 0000000000000..d9977d34c9e0f --- /dev/null +++ b/docs/html/training/multiple-threads/communicate-ui.jd @@ -0,0 +1,263 @@ +page.title=Communicating with the UI Thread + +trainingnavtop=true +@jd:body + +
ThreadSample.zip
++ In the previous lesson you learned how to start a task on a thread managed by + {@link java.util.concurrent.ThreadPoolExecutor}. This final lesson shows you how to send data + from the task to objects running on the user interface (UI) thread. This feature allows your + tasks to do background work and then move the results to UI elements such as bitmaps. +
++ Every app has its own special thread that runs UI objects such as {@link android.view.View} + objects; this thread is called the UI thread. Only objects running on the UI thread have access + to other objects on that thread. Because tasks that you run on a thread from a thread pool + aren't running on your UI thread, they don't have access to UI objects. To move data + from a background thread to the UI thread, use a {@link android.os.Handler} that's + running on the UI thread. +
++ {@link android.os.Handler} is part of the Android system's framework for managing threads. A + {@link android.os.Handler} object receives messages and runs code to handle the messages. + Normally, you create a {@link android.os.Handler} for a new thread, but you can + also create a {@link android.os.Handler} that's connected to an existing thread. + When you connect a {@link android.os.Handler} to your UI thread, the code that handles messages + runs on the UI thread. +
++ Instantiate the {@link android.os.Handler} object in the constructor for the class that + creates your thread pools, and store the object in a global variable. Connect it to the UI + thread by instantiating it with the {@link android.os.Handler#Handler(Looper) Handler(Looper)} + constructor. This constructor uses a {@link android.os.Looper} object, which is another part of + the Android system's thread management framework. When you instantiate a + {@link android.os.Handler} based on a particular {@link android.os.Looper} instance, the + {@link android.os.Handler} runs on the same thread as the {@link android.os.Looper}. + For example: +
+
+private PhotoManager() {
+...
+ // Defines a Handler object that's attached to the UI thread
+ mHandler = new Handler(Looper.getMainLooper()) {
+ ...
+
++ Inside the {@link android.os.Handler}, override the {@link android.os.Handler#handleMessage + handleMessage()} method. The Android system invokes this method when it receives a new message + for a thread it's managing; all of the {@link android.os.Handler} objects for a particular + thread receive the same message. For example: +
+
+ /*
+ * handleMessage() defines the operations to perform when
+ * the Handler receives a new Message to process.
+ */
+ @Override
+ public void handleMessage(Message inputMessage) {
+ // Gets the image task from the incoming Message object.
+ PhotoTask photoTask = (PhotoTask) inputMessage.obj;
+ ...
+ }
+ ...
+ }
+}
+The next section shows how to tell the {@link android.os.Handler} to move data.
+
++ To move data from a task object running on a background thread to an object on the UI thread, + start by storing references to the data and the UI object in the task object. Next, pass the + task object and a status code to the object that instantiated the {@link android.os.Handler}. + In this object, send a {@link android.os.Message} containing the status and the task object to + the {@link android.os.Handler}. Because {@link android.os.Handler} is running on the UI thread, + it can move the data to the UI object. + +
+ For example, here's a {@link java.lang.Runnable}, running on a background thread, that decodes a
+ {@link android.graphics.Bitmap} and stores it in its parent object PhotoTask.
+ The {@link java.lang.Runnable} also stores the status code DECODE_STATE_COMPLETED.
+
+// A class that decodes photo files into Bitmaps
+class PhotoDecodeRunnable implements Runnable {
+ ...
+ PhotoDecodeRunnable(PhotoTask downloadTask) {
+ mPhotoTask = downloadTask;
+ }
+ ...
+ // Gets the downloaded byte array
+ byte[] imageBuffer = mPhotoTask.getByteBuffer();
+ ...
+ // Runs the code for this task
+ public void run() {
+ ...
+ // Tries to decode the image buffer
+ returnBitmap = BitmapFactory.decodeByteArray(
+ imageBuffer,
+ 0,
+ imageBuffer.length,
+ bitmapOptions
+ );
+ ...
+ // Sets the ImageView Bitmap
+ mPhotoTask.setImage(returnBitmap);
+ // Reports a status of "completed"
+ mPhotoTask.handleDecodeState(DECODE_STATE_COMPLETED);
+ ...
+ }
+ ...
+}
+...
+
+
+ PhotoTask also contains a handle to the {@link android.widget.ImageView} that
+ displays the {@link android.graphics.Bitmap}. Even though references to
+ the {@link android.graphics.Bitmap} and {@link android.widget.ImageView} are in the same object,
+ you can't assign the {@link android.graphics.Bitmap} to the {@link android.widget.ImageView},
+ because you're not currently running on the UI thread.
+
+ Instead, the next step is to send this status to the PhotoTask object.
+
+ PhotoTask is the next higher object in the hierarchy. It maintains references to
+ the decoded data and the {@link android.view.View} object that will show the data. It receives
+ a status code from PhotoDecodeRunnable and passes it along to the object that
+ maintains thread pools and instantiates {@link android.os.Handler}:
+
+public class PhotoTask {
+ ...
+ // Gets a handle to the object that creates the thread pools
+ sPhotoManager = PhotoManager.getInstance();
+ ...
+ public void handleDecodeState(int state) {
+ int outState;
+ // Converts the decode state to the overall state.
+ switch(state) {
+ case PhotoDecodeRunnable.DECODE_STATE_COMPLETED:
+ outState = PhotoManager.TASK_COMPLETE;
+ break;
+ ...
+ }
+ ...
+ // Calls the generalized state method
+ handleState(outState);
+ }
+ ...
+ // Passes the state to PhotoManager
+ void handleState(int state) {
+ /*
+ * Passes a handle to this task and the
+ * current state to the class that created
+ * the thread pools
+ */
+ sPhotoManager.handleState(this, state);
+ }
+ ...
+}
+
+
+ From the PhotoTask object, the PhotoManager object receives a status
+ code and a handle to the PhotoTask object. Because the status is
+ TASK_COMPLETE, creates a {@link android.os.Message} containing the state and task
+ object and sends it to the {@link android.os.Handler}:
+
+public class PhotoManager {
+ ...
+ // Handle status messages from tasks
+ public void handleState(PhotoTask photoTask, int state) {
+ switch (state) {
+ ...
+ // The task finished downloading and decoding the image
+ case TASK_COMPLETE:
+ /*
+ * Creates a message for the Handler
+ * with the state and the task object
+ */
+ Message completeMessage =
+ mHandler.obtainMessage(state, photoTask);
+ completeMessage.sendToTarget();
+ break;
+ ...
+ }
+ ...
+ }
+
+
+ Finally, {@link android.os.Handler#handleMessage Handler.handleMessage()} checks the status
+ code for each incoming {@link android.os.Message}. If the status code is
+ TASK_COMPLETE, then the task is finished, and the PhotoTask object
+ in the {@link android.os.Message} contains both a {@link android.graphics.Bitmap} and an
+ {@link android.widget.ImageView}. Because
+ {@link android.os.Handler#handleMessage Handler.handleMessage()} is
+ running on the UI thread, it can safely move the {@link android.graphics.Bitmap} to the
+ {@link android.widget.ImageView}:
+
+ private PhotoManager() {
+ ...
+ mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message inputMessage) {
+ // Gets the task from the incoming Message object.
+ PhotoTask photoTask = (PhotoTask) inputMessage.obj;
+ // Gets the ImageView for this task
+ PhotoView localView = photoTask.getPhotoView();
+ ...
+ switch (inputMessage.what) {
+ ...
+ // The decoding is done
+ case TASK_COMPLETE:
+ /*
+ * Moves the Bitmap from the task
+ * to the View
+ */
+ localView.setImageBitmap(photoTask.getImage());
+ break;
+ ...
+ default:
+ /*
+ * Pass along other messages from the UI
+ */
+ super.handleMessage(inputMessage);
+ }
+ ...
+ }
+ ...
+ }
+ ...
+ }
+...
+}
+
diff --git a/docs/html/training/multiple-threads/create-threadpool.jd b/docs/html/training/multiple-threads/create-threadpool.jd
new file mode 100644
index 0000000000000..4a4ddb1255f8f
--- /dev/null
+++ b/docs/html/training/multiple-threads/create-threadpool.jd
@@ -0,0 +1,238 @@
+page.title=Creating a Manager for Multiple Threads
+
+trainingnavtop=true
+@jd:body
+
+ThreadSample.zip
++ The previous lesson showed how to define a task that executes on a + separate thread. If you only want to run the task once, this may be all you need. If you want + to run a task repeatedly on different sets of data, but you only need one execution running at a + time, an {@link android.app.IntentService} suits your needs. To automatically run tasks + as resources become available, or to allow multiple tasks to run at the same time (or both), + you need to provide a managed collection of threads. To do this, use an instance of + {@link java.util.concurrent.ThreadPoolExecutor}, which runs a task from a queue when a thread + in its pool becomes free. To run a task, all you have to do is add it to the queue. +
+
+ A thread pool can run multiple parallel instances of a task, so you should ensure that your
+ code is thread-safe. Enclose variables that can be accessed by more than one thread in a
+ synchronized block. This approach will prevent one thread from reading the variable
+ while another is writing to it. Typically, this situation arises with static variables, but it
+ also occurs in any object that is only instantiated once. To learn more about this, read the
+
+ Processes and Threads API guide.
+
+
+ Instantiate {@link java.util.concurrent.ThreadPoolExecutor} in its own class. Within this class, + do the following: +
+
+public class PhotoManager {
+ ...
+ static {
+ ...
+ // Creates a single static instance of PhotoManager
+ sInstance = new PhotoManager();
+ }
+ ...
+
+ synchronized block:
+
+public class PhotoManager {
+ ...
+ /**
+ * Constructs the work queues and thread pools used to download
+ * and decode images. Because the constructor is marked private,
+ * it's unavailable to other classes, even in the same package.
+ */
+ private PhotoManager() {
+ ...
+ }
+
+
+public class PhotoManager {
+ ...
+ // Called by the PhotoView to get a photo
+ static public PhotoTask startDownload(
+ PhotoView imageView,
+ boolean cacheFlag) {
+ ...
+ // Adds a download task to the thread pool for execution
+ sInstance.
+ mDownloadThreadPool.
+ execute(downloadTask.getHTTPDownloadRunnable());
+ ...
+ }
+
+
+ private PhotoManager() {
+ ...
+ // Defines a Handler object that's attached to the UI thread
+ mHandler = new Handler(Looper.getMainLooper()) {
+ /*
+ * handleMessage() defines the operations to perform when
+ * the Handler receives a new Message to process.
+ */
+ @Override
+ public void handleMessage(Message inputMessage) {
+ ...
+ }
+ ...
+ }
+ }
+
+ + Once you have the overall class structure, you can start defining the thread pool. To + instantiate a {@link java.util.concurrent.ThreadPoolExecutor} object, you need the + following values: +
+
+public class PhotoManager {
+...
+ /*
+ * Gets the number of available cores
+ * (not always the same as the maximum number of cores)
+ */
+ private static int NUMBER_OF_CORES =
+ Runtime.getRuntime().availableProcessors();
+}
+
+ This number may not reflect the number of physical cores in the device; some devices have
+ CPUs that deactivate one or more cores depending on the system load. For these devices,
+ {@link java.lang.Runtime#availableProcessors availableProcessors()} returns the number of
+ active cores, which may be less than the total number of cores.
+
+public class PhotoManager {
+ ...
+ private PhotoManager() {
+ ...
+ // A queue of Runnables
+ private final BlockingQueue<Runnable> mDecodeWorkQueue;
+ ...
+ // Instantiates the queue of Runnables as a LinkedBlockingQueue
+ mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
+ ...
+ }
+ ...
+}
+
+ + To create a pool of threads, instantiate a thread pool manager by calling + {@link java.util.concurrent.ThreadPoolExecutor#ThreadPoolExecutor ThreadPoolExecutor()}. + This creates and manages a constrained group of threads. Because the initial pool size and + the maximum pool size are the same, {@link java.util.concurrent.ThreadPoolExecutor} creates + all of the thread objects when it is instantiated. For example: +
+
+ private PhotoManager() {
+ ...
+ // Sets the amount of time an idle thread waits before terminating
+ private static final int KEEP_ALIVE_TIME = 1;
+ // Sets the Time Unit to seconds
+ private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
+ // Creates a thread pool manager
+ mDecodeThreadPool = new ThreadPoolExecutor(
+ NUMBER_OF_CORES, // Initial pool size
+ NUMBER_OF_CORES, // Max pool size
+ KEEP_ALIVE_TIME,
+ KEEP_ALIVE_TIME_UNIT,
+ mDecodeWorkQueue);
+ }
+
diff --git a/docs/html/training/multiple-threads/define-runnable.jd b/docs/html/training/multiple-threads/define-runnable.jd
new file mode 100644
index 0000000000000..17640a976a984
--- /dev/null
+++ b/docs/html/training/multiple-threads/define-runnable.jd
@@ -0,0 +1,110 @@
+page.title=Specifying the Code to Run on a Thread
+
+trainingnavtop=true
+@jd:body
+
+ThreadSample.zip
++ This lesson shows you how to implement a {@link java.lang.Runnable} class, which runs the code + in its {@link java.lang.Runnable#run Runnable.run()} method on a separate thread. You can also + pass a {@link java.lang.Runnable} to another object that can then attach it to a thread and + run it. One or more {@link java.lang.Runnable} objects that perform a particular operation are + sometimes called a task. +
++ {@link java.lang.Thread} and {@link java.lang.Runnable} are basic classes that, on their own, + have only limited power. Instead, they're the basis of powerful Android classes such as + {@link android.os.HandlerThread}, {@link android.os.AsyncTask}, and + {@link android.app.IntentService}. {@link java.lang.Thread} and {@link java.lang.Runnable} are + also the basis of the class {@link java.util.concurrent.ThreadPoolExecutor}. This class + automatically manages threads and task queues, and can even run multiple threads in parallel. +
++ Implementing a class that implements {@link java.lang.Runnable} is straightforward. For example: +
+
+public class PhotoDecodeRunnable implements Runnable {
+ ...
+ @Override
+ public void run() {
+ /*
+ * Code you want to run on the thread goes here
+ */
+ ...
+ }
+ ...
+}
+
++ In the class, the {@link java.lang.Runnable#run Runnable.run()} method contains the + code that's executed. Usually, anything is allowable in a {@link java.lang.Runnable}. Remember, + though, that the {@link java.lang.Runnable} won't be running on the UI thread, so it can't + directly modify UI objects such as {@link android.view.View} objects. To communicate with + the UI thread, you have to use the techniques described in the lesson + Communicate with the UI Thread. +
++ At the beginning of the {@link java.lang.Runnable#run run()} method, set the thread to use + background priority by calling + {@link android.os.Process#setThreadPriority Process.setThreadPriority()} with + {@link android.os.Process#THREAD_PRIORITY_BACKGROUND}. This approach reduces + resource competition between the {@link java.lang.Runnable} object's thread and the UI + thread. +
++ You should also store a reference to the {@link java.lang.Runnable} object's + {@link java.lang.Thread} in the {@link java.lang.Runnable} itself, by calling + {@link java.lang.Thread#currentThread() Thread.currentThread()}. +
++ The following snippet shows how to set up the {@link java.lang.Runnable#run run()} method: +
+
+class PhotoDecodeRunnable implements Runnable {
+...
+ /*
+ * Defines the code to run for this task.
+ */
+ @Override
+ public void run() {
+ // Moves the current Thread into the background
+ android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
+ ...
+ /*
+ * Stores the current Thread in the the PhotoTask instance,
+ * so that the instance
+ * can interrupt the Thread.
+ */
+ mPhotoTask.setImageDecodeThread(Thread.currentThread());
+ ...
+ }
+...
+}
+
diff --git a/docs/html/training/multiple-threads/index.jd b/docs/html/training/multiple-threads/index.jd
new file mode 100644
index 0000000000000..3ea57c51b05c9
--- /dev/null
+++ b/docs/html/training/multiple-threads/index.jd
@@ -0,0 +1,83 @@
+page.title=Sending Operations to Multiple Threads
+
+trainingnavtop=true
+startpage=true
+
+
+@jd:body
+
+ThreadSample.zip
++ The speed and efficiency of a long-running, data-intensive operation often improves when you + split it into smaller operations running on multiple threads. On a device that has a CPU with + multiple processors (cores), the system can run the threads in parallel, rather than making each + sub-operation wait for a chance to run. For example, decoding multiple image files in order to + display them on a thumbnail screen runs substantially faster when you do each decode on a + separate thread. +
++ This class shows you how to set up and use multiple threads in an Android app, using a + thread pool object. You'll also learn how to define code to run on a thread and how to + communicate between one of these threads and the UI thread. +
+ThreadSample.zip
++ The previous lesson showed you how to define a class that manages thread pools and the tasks + that run on them. This lesson shows you how to run a task on a thread pool. To do this, + you add the task to the pool's work queue. When a thread becomes available, the + {@link java.util.concurrent.ThreadPoolExecutor} takes a task from the queue and runs it on the + thread. +
++ This lesson also shows you how to stop a task that's running. You might want to do this if a + task starts, but then discovers that its work isn't necessary. Rather than wasting processor + time, you can cancel the thread the task is running on. For example, if you are downloading + images from the network and using a cache, you probably want to stop a task if it detects that + an image is already present in the cache. Depending on how you write your app, you may not be + able to detect this before you start the download. +
++ To start a task object on a thread in a particular thread pool, pass the + {@link java.lang.Runnable} to {@link java.util.concurrent.ThreadPoolExecutor#execute + ThreadPoolExecutor.execute()}. This call adds the task to the thread pool's work queue. When an + idle thread becomes available, the manager takes the task that has been waiting the longest and + runs it on the thread: +
+
+public class PhotoManager {
+ public void handleState(PhotoTask photoTask, int state) {
+ switch (state) {
+ // The task finished downloading the image
+ case DOWNLOAD_COMPLETE:
+ // Decodes the image
+ mDecodeThreadPool.execute(
+ photoTask.getPhotoDecodeRunnable());
+ ...
+ }
+ ...
+ }
+ ...
+}
+
++ When {@link java.util.concurrent.ThreadPoolExecutor} starts a {@link java.lang.Runnable} on a + thread, it automatically calls the object's {@link java.lang.Runnable#run run()} method. +
++ To stop a task, you need to interrupt the task's thread. To prepare to do this, you need to + store a handle to the task's thread when you create the task. For example: +
+
+class PhotoDecodeRunnable implements Runnable {
+ // Defines the code to run for this task
+ public void run() {
+ /*
+ * Stores the current Thread in the
+ * object that contains PhotoDecodeRunnable
+ */
+ mPhotoTask.setImageDecodeThread(Thread.currentThread());
+ ...
+ }
+ ...
+}
+
+
+ To interrupt a thread, call {@link java.lang.Thread#interrupt Thread.interrupt()}. Notice that
+ {@link java.lang.Thread} objects are controlled by the system, which can modify them outside of
+ your app's process. For this reason, you need to lock access on a thread before you
+ interrupt it, by placing the access in a synchronized block. For example:
+
+public class PhotoManager {
+ public static void cancelAll() {
+ /*
+ * Creates an array of Runnables that's the same size as the
+ * thread pool work queue
+ */
+ Runnable[] runnableArray = new Runnable[mDecodeWorkQueue.size()];
+ // Populates the array with the Runnables in the queue
+ mDecodeWorkQueue.toArray(runnableArray);
+ // Stores the array length in order to iterate over the array
+ int len = runnableArray.length;
+ /*
+ * Iterates over the array of Runnables and interrupts each one's Thread.
+ */
+ synchronized (sInstance) {
+ // Iterates over the array of tasks
+ for (int runnableIndex = 0; runnableIndex < len; runnableIndex++) {
+ // Gets the current thread
+ Thread thread = runnableArray[taskArrayIndex].mThread;
+ // if the Thread exists, post an interrupt to it
+ if (null != thread) {
+ thread.interrupt();
+ }
+ }
+ }
+ }
+ ...
+}
+
++ In most cases, {@link java.lang.Thread#interrupt Thread.interrupt()} stops the thread + immediately. However, it only stops threads that are waiting, and will not interrupt CPU or + network-intensive tasks. To avoid slowing down or locking up the system, you should test for + any pending interrupt requests before attempting an operation : +
+
+/*
+ * Before continuing, checks to see that the Thread hasn't
+ * been interrupted
+ */
+if (Thread.interrupted()) {
+ return;
+}
+...
+// Decodes a byte array into a Bitmap (CPU-intensive)
+BitmapFactory.decodeByteArray(
+ imageBuffer, 0, imageBuffer.length, bitmapOptions);
+...
+
diff --git a/docs/html/training/multiple-threads/threadsample.zip b/docs/html/training/multiple-threads/threadsample.zip
new file mode 100644
index 0000000000000..bdc3ccfd7040a
Binary files /dev/null and b/docs/html/training/multiple-threads/threadsample.zip differ
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index c4e0f844cf77d..78b0dce03f72a 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -925,6 +925,33 @@
+