Android Training: Multiple Threads

Change-Id: I58c472aa5ed82f6b4fb50d9bbb4e66841b9e99c3
This commit is contained in:
Joe Malin
2012-11-26 14:06:35 -08:00
parent ba34f097df
commit 8dd6275e28
7 changed files with 869 additions and 0 deletions

View File

@@ -0,0 +1,263 @@
page.title=Communicating with the UI Thread
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<!-- table of contents -->
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#Handler">Define a Handler on the UI Thread</a></li>
<li><a href="#MoveValues">Move Data from a Task to the UI Thread</a>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/components/processes-and-threads.html">Processes and Threads</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="{@docRoot}shareables/training/ThreadSample.zip" class="button">Download the sample</a>
<p class="filename">ThreadSample.zip</p>
</div>
</div>
</div>
<p>
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.
</p>
<p>
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
<em>aren't</em> 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.
</p>
<h2 id="Handler">Define a Handler on the UI Thread</h2>
<p>
{@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.
</p>
<p>
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:
</p>
<pre>
private PhotoManager() {
...
// Defines a Handler object that's attached to the UI thread
mHandler = new Handler(Looper.getMainLooper()) {
...
</pre>
<p>
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:
</p>
<pre>
/*
* handleMessage() defines the operations to perform when
* the Handler receives a new Message to process.
*/
&#64;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.
</pre>
<h2 id="MoveValues">Move Data from a Task to the UI Thread</h2>
<p>
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.
<h3>Store data in the task object</h3>
<p>
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 <code>PhotoTask</code>.
The {@link java.lang.Runnable} also stores the status code <code>DECODE_STATE_COMPLETED</code>.
</p>
<pre>
// 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);
...
}
...
}
...
</pre>
<p>
<code>PhotoTask</code> 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.
</p>
<p>
Instead, the next step is to send this status to the <code>PhotoTask</code> object.
</p>
<h3>Send status up the object hierarchy</h3>
<p>
<code>PhotoTask</code> 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 <code>PhotoDecodeRunnable</code> and passes it along to the object that
maintains thread pools and instantiates {@link android.os.Handler}:
</p>
<pre>
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);
}
...
}
</pre>
<h3>Move data to the UI</h3>
<p>
From the <code>PhotoTask</code> object, the <code>PhotoManager</code> object receives a status
code and a handle to the <code>PhotoTask</code> object. Because the status is
<code>TASK_COMPLETE</code>, creates a {@link android.os.Message} containing the state and task
object and sends it to the {@link android.os.Handler}:
</p>
<pre>
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;
...
}
...
}
</pre>
<p>
Finally, {@link android.os.Handler#handleMessage Handler.handleMessage()} checks the status
code for each incoming {@link android.os.Message}. If the status code is
<code>TASK_COMPLETE</code>, then the task is finished, and the <code>PhotoTask</code> 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}:
</p>
<pre>
private PhotoManager() {
...
mHandler = new Handler(Looper.getMainLooper()) {
&#64;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);
}
...
}
...
}
...
}
...
}
</pre>

View File

@@ -0,0 +1,238 @@
page.title=Creating a Manager for Multiple Threads
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<!-- table of contents -->
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#ClassStructure">Define the Thread Pool Class</a>
<li><a href="#PoolParameters">Determine the Thread Pool Parameters</a></li>
<li><a href="#ThreadPool">Create a Pool of Threads</a></li>
</ol>
<!-- other docs (NOT javadocs) -->
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/components/processes-and-threads.html">Processes and Threads</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="{@docRoot}shareables/training/ThreadSample.zip" class="button">Download the sample</a>
<p class="filename">ThreadSample.zip</p>
</div>
</div>
</div>
<p>
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.
</p>
<p>
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
<code>synchronized</code> 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
<a href="{@docRoot}http://developer.android.com/guide/components/processes-and-threads.html">
Processes and Threads</a> API guide.
</p>
<h2 id="ClassStructure">Define the Thread Pool Class</h2>
<p>
Instantiate {@link java.util.concurrent.ThreadPoolExecutor} in its own class. Within this class,
do the following:
</p>
<dl>
<dt>
Use static variables for thread pools
</dt>
<dd>
You may only want a single instance of a thread pool for your app, in order to have a
single control point for restricted CPU or network resources. If you have different
{@link java.lang.Runnable} types, you may want to have a thread pool for each one, but each
of these can be a single instance. For example, you can add this as part of your
global field declarations:
<pre>
public class PhotoManager {
...
static {
...
// Creates a single static instance of PhotoManager
sInstance = new PhotoManager();
}
...
</pre>
</dd>
<dt>
Use a private constructor
</dt>
<dd>
Making the constructor private ensures that it is a singleton, which means that you don't
have to enclose accesses to the class in a <code>synchronized</code> block:
<pre>
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() {
...
}
</pre>
</dd>
<dt>
Start your tasks by calling methods in the thread pool class.
</dt>
<dd>
Define a method in the thread pool class that adds a task to a thread pool's queue. For
example:
<pre>
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());
...
}
</pre>
</dd>
<dt>
Instantiate a {@link android.os.Handler} in the constructor and attach it to your app's
UI thread.
</dt>
<dd>
A {@link android.os.Handler} allows your app to safely call the methods of UI objects
such as {@link android.view.View} objects. Most UI objects may only be safely altered from
the UI thread. This approach is described in more detail in the lesson
<a href="communicate-ui.html">Communicate with the UI Thread</a>. For example:
<pre>
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.
*/
&#64;Override
public void handleMessage(Message inputMessage) {
...
}
...
}
}
</pre>
</dd>
</dl>
<h2 id="PoolParameters">Determine the Thread Pool Parameters</h2>
<p>
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:
</p>
<dl>
<dt>
Initial pool size and maximum pool size
</dt>
<dd>
The initial number of threads to allocate to the pool, and the maximum allowable number.
The number of threads you can have in a thread pool depends primarily on the number of cores
available for your device. This number is available from the system environment:
<pre>
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();
}
</pre>
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
<i>active</i> cores, which may be less than the total number of cores.
</dd>
<dt>
Keep alive time and time unit
</dt>
<dd>
The duration that a thread will remain idle before it shuts down. The duration is
interpreted by the time unit value, one of the constants defined in
{@link java.util.concurrent.TimeUnit}.
</dd>
<dt>
A queue of tasks
</dt>
<dd>
The incoming queue from which {@link java.util.concurrent.ThreadPoolExecutor} takes
{@link java.lang.Runnable} objects. To start code on a thread, a thread pool manager takes a
{@link java.lang.Runnable} object from a first-in, first-out queue and attaches it to the
thread. You provide this queue object when you create the thread pool, using any queue class
that implements the {@link java.util.concurrent.BlockingQueue} interface. To match the
requirements of your app, you can choose from the available queue implementations; to learn
more about them, see the class overview for {@link java.util.concurrent.ThreadPoolExecutor}.
This example uses the {@link java.util.concurrent.LinkedBlockingQueue} class:
<pre>
public class PhotoManager {
...
private PhotoManager() {
...
// A queue of Runnables
private final BlockingQueue&lt;Runnable&gt; mDecodeWorkQueue;
...
// Instantiates the queue of Runnables as a LinkedBlockingQueue
mDecodeWorkQueue = new LinkedBlockingQueue&lt;Runnable&gt;();
...
}
...
}
</pre>
</dd>
</dl>
<h2 id="ThreadPool">Create a Pool of Threads</h2>
<p>
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:
</p>
<pre>
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);
}
</pre>

View File

@@ -0,0 +1,110 @@
page.title=Specifying the Code to Run on a Thread
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<!-- table of contents -->
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#ExtendClass">Define a Class that Implements Runnable</a></li>
<li><a href="#RunMethod">Implement the run() Method</a>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/components/processes-and-threads.html">Processes and Threads</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="{@docRoot}shareables/training/ThreadSample.zip" class="button">Download the sample</a>
<p class="filename">ThreadSample.zip</p>
</div>
</div>
</div>
</div>
<p>
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 <i>task</i>.
</p>
<p>
{@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.
</p>
<h2 id="ExtendClass">Define a Class that Implements Runnable</h2>
<p>
Implementing a class that implements {@link java.lang.Runnable} is straightforward. For example:
</p>
<pre>
public class PhotoDecodeRunnable implements Runnable {
...
&#64;Override
public void run() {
/*
* Code you want to run on the thread goes here
*/
...
}
...
}
</pre>
<h2 id="RunMethod">Implement the run() Method</h2>
<p>
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
<a href="communicate-ui.html">Communicate with the UI Thread</a>.
</p>
<p>
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.
</p>
<p>
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()}.
</p>
<p>
The following snippet shows how to set up the {@link java.lang.Runnable#run run()} method:
</p>
<pre>
class PhotoDecodeRunnable implements Runnable {
...
/*
* Defines the code to run for this task.
*/
&#64;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());
...
}
...
}
</pre>

View File

@@ -0,0 +1,83 @@
page.title=Sending Operations to Multiple Threads
trainingnavtop=true
startpage=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
<h2>Dependencies and prerequisites</h2>
<ul>
<li>Android 3.0 (API Level 11) or higher</li>
<li>
<a href="{@docRoot}training/load-data-background/index.html">
Loading Data in the Background</a> training class
</li>
<li>
<a href="{@docRoot}training/run-background-service/index.html">
Running in a Background Service</a> training class
</li>
</ul>
<!-- related docs (NOT javadocs) -->
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/components/processes-and-threads.html">Processes and Threads</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="{@docRoot}shareables/training/ThreadSample.zip" class="button">Download the sample</a>
<p class="filename">ThreadSample.zip</p>
</div>
</div>
</div>
<p>
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.
</p>
<p>
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.
</p>
<h2>Lessons</h2>
<dl>
<dt>
<b><a href="define-runnable.html">Specifying the Code to Run on a Thread</a></b>
</dt>
<dd>
Learn how to write code to run on a separate {@link java.lang.Thread}, by
defining a class that implements the {@link java.lang.Runnable} interface.
</dd>
<dt>
<b><a href="create-threadpool.html">Creating a Manager for Multiple Threads</a></b>
</dt>
<dd>
Learn how to create an object that manages a pool of {@link java.lang.Thread} objects and
a queue of {@link java.lang.Runnable} objects. This object is called a
{@link java.util.concurrent.ThreadPoolExecutor}.
</dd>
<dt>
<b><a href="run-code.html">Running Code on a Thread Pool Thread</a></b>
</dt>
<dd>
Learn how to run a {@link java.lang.Runnable} on a thread from the thread pool.
</dd>
<dt>
<b><a href="communicate-ui.html">Communicating with the UI Thread</a></b>
</dt>
<dd>
Learn how to communicate from a thread in the thread pool to the UI thread.
</dd>
</dl>

View File

@@ -0,0 +1,148 @@
page.title=Running Code on a Thread Pool Thread
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<!-- table of contents -->
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#RunRunnable">Run a Runnable on a Thread in the Thread Pool</a></li>
<li><a href="#StopThread">Interrupt Running Code</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/components/processes-and-threads.html">Processes and Threads</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="{@docRoot}shareables/training/ThreadSample.zip" class="button">Download the sample</a>
<p class="filename">ThreadSample.zip</p>
</div>
</div>
</div>
<p>
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.
</p>
<p>
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.
</p>
<h2 id="RunRunnable">Run a Task on a Thread in the Thread Pool</h2>
<p>
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:
</p>
<pre>
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());
...
}
...
}
...
}
</pre>
<p>
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.
</p>
<h2 id="StopThread">Interrupt Running Code</h2>
<p>
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:
</p>
<pre>
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());
...
}
...
}
</pre>
<p>
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 <code>synchronized</code> block. For example:
</p>
<pre>
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 &lt; 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();
}
}
}
}
...
}
</pre>
<p>
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 :
</p>
<pre>
/*
* 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);
...
</pre>

Binary file not shown.

View File

@@ -907,6 +907,33 @@
</li>
</ul>
</li>
<li class="nav-section">
<div class="nav-section-header">
<a href="<?cs var:toroot ?>training/multiple-threads/index.html"
description=
"How to improve the performance and scalability of long-running operations by
dispatching work to multiple threads.">
Sending Operations to Multiple Threads</a>
</div>
<ul>
<li><a href="<?cs var:toroot ?>training/multiple-threads/define-runnable.html">
Specifying the Code to Run on a Thread
</a>
</li>
<li><a href="<?cs var:toroot ?>training/multiple-threads/create-threadpool.html">
Creating a Manager for Multiple Threads
</a>
</li>
<li><a href="<?cs var:toroot ?>training/multiple-threads/run-code.html">
Running Code on a Thread Pool Thread
</a>
</li>
<li><a href="<?cs var:toroot ?>training/multiple-threads/communicate-ui.html">
Communicating with the UI Thread
</a>
</li>
</ul>
</li>
<li>
<a href="<?cs var:toroot ?>training/perf-anr.html"