The Android background thread communicates with the UI thread instance

  • 2020-06-01 11:01:10
  • OfStack

This section shows you how to send data from a task to an object in an UI thread. This feature allows you to work in a background thread and then show the results in an UI thread.

Define 1 Handler in the UI thread

Handler is part of the thread management framework of the Android system. An Handler object receives the message and runs the code to process the message. Normally, you create an Handler for a new thread, but you can also create an Handler for an existing thread. When you connect an Handler thread to an UI thread, the code that handles the message will run on the UI thread.

Instantiate the Handler object in the constructor of the class that created the thread pool and store it in the global variable. The Handler(Looper) method is instantiated, connected to the UI thread, and the Looper object is constructed using the Looper object, which is part of the Android thread management framework. Such as:


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

In Handler, override handleMessage(). The Android system calls this method when a thread managed by Handler receives a new message. All Handler objects in a given thread receive the same message.


        /*
         * 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;
            ...
        }
    ...
    }
}

Move data from task to UI thread

To move data from a background thread task to an UI thread object, save the reference to the UI object of the data and task object, and pass the task object and status code to the Handler object. Within this object, send a message containing the status and task object to Handler. Since Handler is running on the UI thread, it can move data to UI objects.

Store data in the task object

For example, this is an Runnable running in the background thread, which parses Bitmap and saves it to its parent object. Runnable also saves the status code DECODE_STATE_COMPLETED.


// 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);
        ...
    }
    ...
}
...

PhotoTask also contains an ImageView reference to display Bitmap. Although the Bitmap and ImageView references are in the same object, you cannot directly ask ImageView to display Bitmap because it is not in the UI thread.

Send state level by level along the object hierarchy

PhotoTask holds a reference to the decoded data and the View object that displays the data, which receives the status code from PhotoDecodeRunnable and passes along the referenced objects and Handler instances in the thread pool.


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);
    }
    ...
}

Move data to UI

PhotoManager receives a status code from the PhotoTask object and a handle to the PhotoTask object. Since the state is TASK_COMPLETE, create an Message containing the state and task object and send it to Handler.


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;
            ...
        }
        ...
    }

Finally, Handler.handleMessage () checks the status code for each incoming Message. If the status code is TASK_COMPLETE, the task is done. The PhotoTask object in Message contains Bitmap and ImageView. Since Handler.handleMessage () runs in the UI thread, it can safely set Bitmap for ImageView.


Related articles: