Android 线程池系列教程(5)与UI线程通信要用Handler

Communicating with the UI Thread

1.This lesson teaches you to

  1. Define a Handler on the UI Thread
  2. Move Data from a Task to the UI Thread

2.You should also read

3.Try it out

  DOWNLOAD THE SAMPLE  ThreadSample.zip

  In the previous lesson you learned how to start a task on a thread managed by 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.

  Every app has its own special thread that runs UI objects such as 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 aren't 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 Handler that's running on the UI thread.

4.Define a Handler on the UI Thread

  Handler is part of the Android system's framework for managing threads. A Handler object receives messages and runs code to handle the messages. Normally, you create a Handler for a new thread, but you can also create a Handler that's connected to an existing thread. When you connect a Handler to your UI thread, the code that handles messages runs on the UI thread.

  Instantiate the 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 Handler(Looper)constructor. This constructor uses a Looper object, which is another part of the Android system's thread management framework. When you instantiate a Handler based on a particular Looper instance, theHandler runs on the same thread as the Looper. For example:

1   private PhotoManager() {
2   ...
3        // Defines a Handler object that's attached to the UI thread
4        mHandler = new Handler(Looper.getMainLooper()) {
5        ...

  Inside the Handler, override the handleMessage() method. The Android system invokes this method when it receives a new message for a thread it's managing; all of the Handler objects for a particular thread receive the same message. For example:

 1       /*
 2          * handleMessage() defines the operations to perform when
 3          * the Handler receives a new Message to process.
 4          */
 5         @Override
 6         public void handleMessage(Message inputMessage) {
 7             // Gets the image task from the incoming Message object.
 8             PhotoTask photoTask = (PhotoTask) inputMessage.obj;
 9             ...
10         }
11     ...
12     }
13 }

  The next section shows how to tell the Handler to move data.

5.Move Data from a Task to the UI Thread

  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 Handler. In this object, send a Message containing the status and the task object to the Handler.   Because Handler is running on the UI thread, it can move the data to the UI object.

5.1Store data in the task object

  For example, here's a Runnable, running on a background thread, that decodes a Bitmap and stores it in its parent object PhotoTask. The Runnable also stores the status code DECODE_STATE_COMPLETED.

 1 // A class that decodes photo files into Bitmaps
 2 class PhotoDecodeRunnable implements Runnable {
 3     ...
 4     PhotoDecodeRunnable(PhotoTask downloadTask) {
 5         mPhotoTask = downloadTask;
 6     }
 7     ...
 8     // Gets the downloaded byte array
 9     byte[] imageBuffer = mPhotoTask.getByteBuffer();
10     ...
11     // Runs the code for this task
12     public void run() {
13         ...
14         // Tries to decode the image buffer
15         returnBitmap = BitmapFactory.decodeByteArray(
16                 imageBuffer,
17                 0,
18                 imageBuffer.length,
19                 bitmapOptions
20         );
21         ...
22         // Sets the ImageView Bitmap
23         mPhotoTask.setImage(returnBitmap);
24         // Reports a status of "completed"
25         mPhotoTask.handleDecodeState(DECODE_STATE_COMPLETED);
26         ...
27     }
28     ...
29 }
30 ...

  PhotoTask also contains a handle to the ImageView that displays the Bitmap. Even though references to the Bitmap and ImageView are in the same object, you can't assign the Bitmap to the ImageView, because you're not currently running on the UI thread.

  Instead, the next step is to send this status to the PhotoTask object.

5.2 Send status up the object hierarchy

  PhotoTask is the next higher object in the hierarchy. It maintains references to the decoded data and theView object that will show the data. It receives a status code from PhotoDecodeRunnable and passes it along to the object that maintains thread pools and instantiates Handler:

 1 public class PhotoTask {
 2     ...
 3     // Gets a handle to the object that creates the thread pools
 4     sPhotoManager = PhotoManager.getInstance();
 5     ...
 6     public void handleDecodeState(int state) {
 7         int outState;
 8         // Converts the decode state to the overall state.
 9         switch(state) {
10             case PhotoDecodeRunnable.DECODE_STATE_COMPLETED:
11                 outState = PhotoManager.TASK_COMPLETE;
12                 break;
13             ...
14         }
15         ...
16         // Calls the generalized state method
17         handleState(outState);
18     }
19     ...
20     // Passes the state to PhotoManager
21     void handleState(int state) {
22         /*
23          * Passes a handle to this task and the
24          * current state to the class that created
25          * the thread pools
26          */
27         sPhotoManager.handleState(this, state);
28     }
29     ...
30 }

5.3 Move data to the UI

  From the PhotoTask object, the PhotoManager object receives a status code and a handle to thePhotoTask object. Because the status is TASK_COMPLETE, creates a Message containing the state and task object and sends it to the Handler:

 1 public class PhotoManager {
 2     ...
 3     // Handle status messages from tasks
 4     public void handleState(PhotoTask photoTask, int state) {
 5         switch (state) {
 6             ...
 7             // The task finished downloading and decoding the image
 8             case TASK_COMPLETE:
 9                 /*
10                  * Creates a message for the Handler
11                  * with the state and the task object
12                  */
13                 Message completeMessage =
14                         mHandler.obtainMessage(state, photoTask);
15                 completeMessage.sendToTarget();
16                 break;
17             ...
18         }
19         ...
20     }

  Finally, Handler.handleMessage() checks the status code for each incoming Message. If the status code isTASK_COMPLETE, then the task is finished, and the PhotoTask object in the Message contains both aBitmap and an ImageView. Because Handler.handleMessage() is running on the UI thread, it can safely move the Bitmap to the ImageView: 

 1  private PhotoManager() {
 2         ...
 3             mHandler = new Handler(Looper.getMainLooper()) {
 4                 @Override
 5                 public void handleMessage(Message inputMessage) {
 6                     // Gets the task from the incoming Message object.
 7                     PhotoTask photoTask = (PhotoTask) inputMessage.obj;
 8                     // Gets the ImageView for this task
 9                     PhotoView localView = photoTask.getPhotoView();
10                     ...
11                     switch (inputMessage.what) {
12                         ...
13                         // The decoding is done
14                         case TASK_COMPLETE:
15                             /*
16                              * Moves the Bitmap from the task
17                              * to the View
18                              */
19                             localView.setImageBitmap(photoTask.getImage());
20                             break;
21                         ...
22                         default:
23                             /*
24                              * Pass along other messages from the UI
25                              */
26                             super.handleMessage(inputMessage);
27                     }
28                     ...
29                 }
30                 ...
31             }
32             ...
33     }
34 ...
35 }

 

 

posted @ 2015-07-28 19:16  f9q  阅读(304)  评论(0编辑  收藏  举报