Home | History | Annotate | Download | only in multiple-threads
      1 page.title=Communicating with the UI Thread
      2 
      3 trainingnavtop=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>
     64 private 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 }
     90 The 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
    109 class 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>
    157 public 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>
    196 public 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