Android Thread Message Looper Handler Example

Android’s message queue and queue looper is aimed at specific thread, a thread can have it’s own message queue and queue looper.

1. Android Message Queue And Looper Introduction.

If you want to send messages between different thread, you need to add a message object in that thread’s message queue. Then the queue looper will fetch the message and process it.

android handler looper architecture diagramIn android development, Activity is commonly used as the main thread. Android OS will create message queue and queue looper for the main thread automatically.

So you can use Handler to send message to the Activity class to let it modify the UI component. Because UI component is thread unsafe, only main thread can modify it. Please read Android Handler Example to learn more.

2. How To Create Child Thread’s Message Queue And Looper.

However, the worker thread created by default has no message queue and message looper, If you want the worker thread to have a message queue and message looper, you can follow below steps.

  1. Call Looper.prepare() to create the message queue in the thread.
  2. Create a thread specified Handler which handle messages in the message queue.
  3. Call Looper.loop() to enter the message loop.
  4. If you want the worker thread quit message loop, please call Handler.getLooper().quit().

3. Android Child Thread Message Queue And Looper Example.

android thread message looper handler exanple

From above picture, you can see below steps.

  1. When the first two button are clicked, main thread will send a message object to worker thread message queue.
  2. Worker thread read the message object out from the queue and send a message to main thread also.
  3. Main thread will display different text according to worker thread sent message.
  4. After the “quit child thread looper” button is clicked, worker thread message looper stopped. And worker thread can not handle any messages. So the text view content will not change also.
READ :   Java Multiple Thread Overview

activity_child_thread_handler_looper.xml

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:layout_editor_absoluteY="8dp"
    tools:layout_editor_absoluteX="8dp">

    <Button
        android:id="@+id/runTaskOneButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Run Task One" />

    <Button
        android:id="@+id/runTaskTwoButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Run Task Two" />

    <Button
        android:id="@+id/quitChildThreaLooperButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Quit Child Thread Looper" />

    <TextView
        android:id="@+id/taskStatusTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="" />
</LinearLayout>

ChildThreadHandlerLooperActivity.java

package com.dev2qa.example;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class ChildThreadHandlerLooperActivity extends AppCompatActivity {

    private int MAIN_THREAD_TASK_1 = 1;
    private int MAIN_THREAD_TASK_2 = 2;
    private int CHILD_THREAD_QUIT_LOOPER = 3;

    private Handler mainThreadHandler;

    private MyWorkerThread workerThread = null;

    private Button runTaskOneButton;

    private Button runTaskTwoButton;

    private TextView taskStatusTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_child_thread_handler_looper);

        setTitle("dev2qa.com - Child Thread Looper Handler Example");

        // Create and start the worker thread.
        workerThread = new MyWorkerThread();
        workerThread.start();

        // Handle message from main thread message queue.
        mainThreadHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                Log.i("MAIN_THREAD", "Receive message from child thread.");
                if(msg.what == MAIN_THREAD_TASK_1)
                {
                    // If task one button is clicked.
                    taskStatusTextView.setText("Task one execute.");
                }else if(msg.what == MAIN_THREAD_TASK_2)
                {
                    // If task two button is clicked.
                    taskStatusTextView.setText("Task two execute.");
                }else if(msg.what == CHILD_THREAD_QUIT_LOOPER)
                {
                    // If quit child thread looper button is clicked.
                    taskStatusTextView.setText("Quit child thread looper.");
                }
            }
        };

        // Get run task buttons.
        runTaskOneButton = (Button)findViewById(R.id.runTaskOneButton);
        runTaskTwoButton = (Button)findViewById(R.id.runTaskTwoButton);

        // Set on click listener to each button.
        runTaskOneButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // When click this button, create a message object.
                Message msg = new Message();
                msg.what = MAIN_THREAD_TASK_1;
                // Use worker thread message Handler to put message into worker thread message queue.
                workerThread.workerThreadHandler.sendMessage(msg);
            }
        });

        // Please see comments for runTaskOneButton.
        runTaskTwoButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Message msg = new Message();
                msg.what = MAIN_THREAD_TASK_2;
                workerThread.workerThreadHandler.sendMessage(msg);
            }
        });

        // Get status info TextView object.
        taskStatusTextView = (TextView)findViewById(R.id.taskStatusTextView);

        // Get the quit child thread looper button.
        Button quitChildThreadLooperButton = (Button)findViewById(R.id.quitChildThreaLooperButton);
        quitChildThreadLooperButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Click this button will quit child thread looper.
                workerThread.workerThreadHandler.getLooper().quit();
            }
        });
    }

    // This child thread class has it's own Looper and Handler object.
    private class MyWorkerThread extends Thread{
        // This is worker thread handler.
        public Handler workerThreadHandler;

        @Override
        public void run() {
            // Prepare child thread Lopper object.
            Looper.prepare();

            // Create child thread Handler.
            workerThreadHandler = new Handler(Looper.myLooper()){
                @Override
                public void handleMessage(Message msg) {
                    // When child thread handler get message from child thread message queue.
                    Log.i("CHILD_THREAD", "Receive message from main thread.");
                    Message message = new Message();
                    message.what = msg.what;
                    // Send the message back to main thread message queue use main thread message Handler.
                    mainThreadHandler.sendMessage(message);
                }
            };
            // Loop the child thread message queue.
            Looper.loop();

            // The code after Looper.loop() will not be executed until you call workerThreadHandler.getLooper().quit()
            Log.i("CHILD_THREAD", "This log is printed after Looper.loop() method. Only when this thread loop quit can this log be printed.");
            // Send a message to main thread.
            Message msg = new Message();
            msg.what = CHILD_THREAD_QUIT_LOOPER;
            mainThreadHandler.sendMessage(msg);
        }
    }
}

Android Studio Logcat Console Output

From the logcat console output, we can see that before worker thread looper’s quit() method is called, the code after Looper.loop() in child thread will not be executed.

READ :   Android Activity Best Practice

android logcat console output for main thread looper

android logcat console output for child thread looper

(Visited 2,232 times, 5 visits today)

1 Comment


  1. Thank your for your share,
    I’m confused about looper, handler, message issue and search for some example to learn about it.

    Your example is simple but very useful to me.
    Thank you very much!!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.