am 8dd6275e: Android Training: Multiple Threads
* commit '8dd6275e2871bea1acb20cbdd98ea0451b3d5be0': Android Training: Multiple Threads
This commit is contained in:
263
docs/html/training/multiple-threads/communicate-ui.jd
Normal file
263
docs/html/training/multiple-threads/communicate-ui.jd
Normal 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.
|
||||||
|
*/
|
||||||
|
@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()) {
|
||||||
|
@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>
|
||||||
238
docs/html/training/multiple-threads/create-threadpool.jd
Normal file
238
docs/html/training/multiple-threads/create-threadpool.jd
Normal 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.
|
||||||
|
*/
|
||||||
|
@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<Runnable> mDecodeWorkQueue;
|
||||||
|
...
|
||||||
|
// Instantiates the queue of Runnables as a LinkedBlockingQueue
|
||||||
|
mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
|
||||||
|
...
|
||||||
|
}
|
||||||
|
...
|
||||||
|
}
|
||||||
|
</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>
|
||||||
110
docs/html/training/multiple-threads/define-runnable.jd
Normal file
110
docs/html/training/multiple-threads/define-runnable.jd
Normal 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 {
|
||||||
|
...
|
||||||
|
@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.
|
||||||
|
*/
|
||||||
|
@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>
|
||||||
83
docs/html/training/multiple-threads/index.jd
Normal file
83
docs/html/training/multiple-threads/index.jd
Normal 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>
|
||||||
|
|
||||||
148
docs/html/training/multiple-threads/run-code.jd
Normal file
148
docs/html/training/multiple-threads/run-code.jd
Normal 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 < 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>
|
||||||
BIN
docs/html/training/multiple-threads/threadsample.zip
Normal file
BIN
docs/html/training/multiple-threads/threadsample.zip
Normal file
Binary file not shown.
@@ -907,6 +907,33 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</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>
|
<li>
|
||||||
<a href="<?cs var:toroot ?>training/perf-anr.html"
|
<a href="<?cs var:toroot ?>training/perf-anr.html"
|
||||||
|
|||||||
Reference in New Issue
Block a user