• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Communicating with the UI Thread
2
3trainingnavtop=true
4@jd:body
5
6<div id="tb-wrapper">
7<div id="tb">
8
9<!-- table of contents -->
10<h2>This lesson teaches you to</h2>
11<ol>
12  <li><a href="#Handler">Define a Handler on the UI Thread</a></li>
13  <li><a href="#MoveValues">Move Data from a Task to the UI Thread</a>
14</ol>
15
16<h2>You should also read</h2>
17<ul>
18  <li><a href="{@docRoot}guide/components/processes-and-threads.html">Processes and Threads</a></li>
19</ul>
20
21
22<h2>Try it out</h2>
23<div class="download-box">
24    <a href="{@docRoot}shareables/training/ThreadSample.zip" class="button">Download the sample</a>
25    <p class="filename">ThreadSample.zip</p>
26</div>
27
28</div>
29</div>
30<p>
31    In the previous lesson you learned how to start a task on a thread managed by
32    {@link java.util.concurrent.ThreadPoolExecutor}. This final lesson shows you how to send data
33    from the task to objects running on the user interface (UI) thread. This feature allows your
34    tasks to do background work and then move the results to UI elements such as bitmaps.
35</p>
36<p>
37    Every app has its own special thread that runs UI objects such as {@link android.view.View}
38    objects; this thread is called the UI thread. Only objects running on the UI thread have access
39    to other objects on that thread. Because tasks that you run on a thread from a thread pool
40    <em>aren't</em> running on your UI thread, they don't have access to UI objects. To move data
41    from a background thread to the UI thread, use a {@link android.os.Handler} that's
42    running on the UI thread.
43</p>
44<h2 id="Handler">Define a Handler on the UI Thread</h2>
45<p>
46    {@link android.os.Handler} is part of the Android system's framework for managing threads. A
47    {@link android.os.Handler} object receives messages and runs code to handle the messages.
48    Normally, you create a {@link android.os.Handler} for a new thread, but you can
49    also create a {@link android.os.Handler} that's connected to an existing thread.
50    When you connect a {@link android.os.Handler} to your UI thread, the code that handles messages
51    runs on the UI thread.
52</p>
53<p>
54    Instantiate the {@link android.os.Handler} object in the constructor for the class that
55    creates your thread pools, and store the object in a global variable. Connect it to the UI
56    thread by instantiating it with the {@link android.os.Handler#Handler(Looper) Handler(Looper)}
57    constructor. This constructor uses a {@link android.os.Looper} object, which is another part of
58    the Android system's thread management framework. When you instantiate a
59    {@link android.os.Handler} based on a particular {@link android.os.Looper} instance, the
60    {@link android.os.Handler} runs on the same thread as the {@link android.os.Looper}.
61    For example:
62</p>
63<pre>
64private PhotoManager() {
65...
66    // Defines a Handler object that's attached to the UI thread
67    mHandler = new Handler(Looper.getMainLooper()) {
68    ...
69</pre>
70<p>
71    Inside the {@link android.os.Handler}, override the {@link android.os.Handler#handleMessage
72    handleMessage()} method. The Android system invokes this method when it receives a new message
73    for a thread it's managing; all of the {@link android.os.Handler} objects for a particular
74    thread receive the same message. For example:
75</p>
76<pre>
77        /*
78         * handleMessage() defines the operations to perform when
79         * the Handler receives a new Message to process.
80         */
81        &#64;Override
82        public void handleMessage(Message inputMessage) {
83            // Gets the image task from the incoming Message object.
84            PhotoTask photoTask = (PhotoTask) inputMessage.obj;
85            ...
86        }
87    ...
88    }
89}
90The next section shows how to tell the {@link android.os.Handler} to move data.
91</pre>
92<h2 id="MoveValues">Move Data from a Task to the UI Thread</h2>
93<p>
94    To move data from a task object running on a background thread to an object on the UI thread,
95    start by storing references to the data and the UI object in the task object. Next, pass the
96    task object and a status code to the object that instantiated the {@link android.os.Handler}.
97    In this object, send a {@link android.os.Message} containing the status and the task object to
98    the {@link android.os.Handler}. Because {@link android.os.Handler} is running on the UI thread,
99    it can move the data to the UI object.
100
101<h3>Store data in the task object</h3>
102<p>
103    For example, here's a {@link java.lang.Runnable}, running on a background thread, that decodes a
104    {@link android.graphics.Bitmap} and stores it in its parent object <code>PhotoTask</code>.
105    The {@link java.lang.Runnable} also stores the status code <code>DECODE_STATE_COMPLETED</code>.
106</p>
107<pre>
108// A class that decodes photo files into Bitmaps
109class PhotoDecodeRunnable implements Runnable {
110    ...
111    PhotoDecodeRunnable(PhotoTask downloadTask) {
112        mPhotoTask = downloadTask;
113    }
114    ...
115    // Gets the downloaded byte array
116    byte[] imageBuffer = mPhotoTask.getByteBuffer();
117    ...
118    // Runs the code for this task
119    public void run() {
120        ...
121        // Tries to decode the image buffer
122        returnBitmap = BitmapFactory.decodeByteArray(
123                imageBuffer,
124                0,
125                imageBuffer.length,
126                bitmapOptions
127        );
128        ...
129        // Sets the ImageView Bitmap
130        mPhotoTask.setImage(returnBitmap);
131        // Reports a status of "completed"
132        mPhotoTask.handleDecodeState(DECODE_STATE_COMPLETED);
133        ...
134    }
135    ...
136}
137...
138</pre>
139<p>
140    <code>PhotoTask</code> also contains a handle to the {@link android.widget.ImageView} that
141    displays the {@link android.graphics.Bitmap}. Even though references to
142    the {@link android.graphics.Bitmap} and {@link android.widget.ImageView} are in the same object,
143    you can't assign the {@link android.graphics.Bitmap} to the {@link android.widget.ImageView},
144    because you're not currently running on the UI thread.
145</p>
146<p>
147    Instead, the next step is to send this status to the <code>PhotoTask</code> object.
148</p>
149<h3>Send status up the object hierarchy</h3>
150<p>
151    <code>PhotoTask</code> is the next higher object in the hierarchy. It maintains references to
152    the decoded data and the {@link android.view.View} object that will show the data. It receives
153    a status code from <code>PhotoDecodeRunnable</code> and passes it along to the object that
154    maintains thread pools and instantiates {@link android.os.Handler}:
155</p>
156<pre>
157public class PhotoTask {
158    ...
159    // Gets a handle to the object that creates the thread pools
160    sPhotoManager = PhotoManager.getInstance();
161    ...
162    public void handleDecodeState(int state) {
163        int outState;
164        // Converts the decode state to the overall state.
165        switch(state) {
166            case PhotoDecodeRunnable.DECODE_STATE_COMPLETED:
167                outState = PhotoManager.TASK_COMPLETE;
168                break;
169            ...
170        }
171        ...
172        // Calls the generalized state method
173        handleState(outState);
174    }
175    ...
176    // Passes the state to PhotoManager
177    void handleState(int state) {
178        /*
179         * Passes a handle to this task and the
180         * current state to the class that created
181         * the thread pools
182         */
183        sPhotoManager.handleState(this, state);
184    }
185    ...
186}
187</pre>
188<h3>Move data to the UI</h3>
189<p>
190    From the <code>PhotoTask</code> object, the <code>PhotoManager</code> object receives a status
191    code and a handle to the <code>PhotoTask</code> object. Because the status is
192    <code>TASK_COMPLETE</code>, creates a {@link android.os.Message} containing the state and task
193    object and sends it to the {@link android.os.Handler}:
194</p>
195<pre>
196public class PhotoManager {
197    ...
198    // Handle status messages from tasks
199    public void handleState(PhotoTask photoTask, int state) {
200        switch (state) {
201            ...
202            // The task finished downloading and decoding the image
203            case TASK_COMPLETE:
204                /*
205                 * Creates a message for the Handler
206                 * with the state and the task object
207                 */
208                Message completeMessage =
209                        mHandler.obtainMessage(state, photoTask);
210                completeMessage.sendToTarget();
211                break;
212            ...
213        }
214        ...
215    }
216</pre>
217<p>
218    Finally, {@link android.os.Handler#handleMessage Handler.handleMessage()} checks the status
219    code for each incoming {@link android.os.Message}. If the status code is
220    <code>TASK_COMPLETE</code>, then the task is finished, and the <code>PhotoTask</code> object
221    in the {@link android.os.Message} contains both a {@link android.graphics.Bitmap} and an
222    {@link android.widget.ImageView}. Because
223    {@link android.os.Handler#handleMessage Handler.handleMessage()} is
224    running on the UI thread, it can safely move the {@link android.graphics.Bitmap} to the
225    {@link android.widget.ImageView}:
226</p>
227<pre>
228    private PhotoManager() {
229        ...
230            mHandler = new Handler(Looper.getMainLooper()) {
231                &#64;Override
232                public void handleMessage(Message inputMessage) {
233                    // Gets the task from the incoming Message object.
234                    PhotoTask photoTask = (PhotoTask) inputMessage.obj;
235                    // Gets the ImageView for this task
236                    PhotoView localView = photoTask.getPhotoView();
237                    ...
238                    switch (inputMessage.what) {
239                        ...
240                        // The decoding is done
241                        case TASK_COMPLETE:
242                            /*
243                             * Moves the Bitmap from the task
244                             * to the View
245                             */
246                            localView.setImageBitmap(photoTask.getImage());
247                            break;
248                        ...
249                        default:
250                            /*
251                             * Pass along other messages from the UI
252                             */
253                            super.handleMessage(inputMessage);
254                    }
255                    ...
256                }
257                ...
258            }
259            ...
260    }
261...
262}
263</pre>
264