SoFunction
Updated on 2025-03-11

Android background thread and UI thread communication examples

This section shows you how to send data to objects in the UI thread in the task. This feature allows you to work in the background thread and display the results in the UI thread after completion.

Define a Handler in the UI thread

Handler is part of the Android system thread management framework. A Handler object receives the message and runs the code to process the message. Normally, you create a Handler for a new thread, but you can also create a Handler for an existing thread. When you connect the Handler to the 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 creates the thread pool and save it in a global variable. Instantiated with Handler (Looper) method, connect to the UI thread, and constructed using Looper object, which is also part of the Android system thread management framework. The Looper class has a static method getMainLooper() to get the Looper object of the UI thread. like:

Copy the codeThe code is as follows:

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

In Handler, override handleMessage(). The Android system will call this method when the thread managed by the Handler receives a new message. All Handler objects of a specified thread will receive the same message.

Copy the codeThe code is as follows:

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

Move data from task to UI thread

To move data from the task of the background thread to the object of the UI thread, first save the UI object referenced to the data and task object, and then pass the task object and status code to the Handler object. In this object, send a message containing the status and task objects to the Handler. Because the Handler runs on the UI thread, it can move data to the UI object.

Store data in task objects

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

Copy the codeThe code is as follows:

// A class that decodes photo files into Bitmaps
class PhotoDecodeRunnable implements Runnable {
    ...
    PhotoDecodeRunnable(PhotoTask downloadTask) {
        mPhotoTask = downloadTask;
    }
    ...
    // Gets the downloaded byte array
    byte[] imageBuffer = ();
    ...
    // Runs the code for this task
    public void run() {
        ...
        // Tries to decode the image buffer
        returnBitmap = (
                imageBuffer,
                0,
                ,
                bitmapOptions
        );
        ...
        // Sets the ImageView Bitmap
        (returnBitmap);
        // Reports a status of "completed"
        (DECODE_STATE_COMPLETED);
        ...
    }
    ...
}
...

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

Send status step by step along the object hierarchy

PhotoTask holds references to the decoded data and the View object that displays the data. It receives the status code from the PhotoDecodeRunnable and transmits it along the referenced objects and Handler instances in the thread pool.

Copy the codeThe code is as follows:

public class PhotoTask {
    ...
    // Gets a handle to the object that creates the thread pools
    sPhotoManager = ();
    ...
    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
         */
        (this, state);
    }
    ...
}

Move data to UI

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

Copy the codeThe code is as follows:

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 =
                        (state, photoTask);
                ();
                break;
            ...
        }
        ...
    }

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