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 @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 @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