Understanding Handler in Android

Whenever your app starts up in Android, all components are run on a single primary thread (by default) called the main thread. The primary role of this thread though, is to handle the user interface and dispatch events to the appropriate widgets/views. For this reason, the main thread is also referred to as the UI thread.

We can visualize a thread as a column of instructions or messages executed by the CPU. These instructions come from different places; they come from our application as well as the OS. This thread is used to handle the response from the user, lifecycle methods, and system callbacks. The CPU processes messages sequentially, one after another; if it's busy, the message will wait in a queue to be executed. Therefore, if we perform long operations in our application and send many messages to the CPU, we will not let UI messages be executed, and this will result in the mobile not responding for the user.

After multiple threads run concurrently, such as a main application thread and a background thread, there needs to be a way for them to communicate. Example use cases for such communication are:

  • A main thread serves time-critical information and passes messages to the background time-consuming thread to update.
  • A large computation completes and sends a message back to the calling thread with the result.

Some famous Android constructs you’ve probably come across:

  1. Handler
  2. AsyncTask
  3. Service

Let's look at bunch of Handler, Message, MessageQueue and Looper.

Each main thread comes with following objects:

  • A MessageQueue, which, as its name suggests, is a queue of Messages. Any action performed by your app is, at its core, a simple Message that was enqueued to the MessageQueue. MessageQueue contains a list of Messages or Runnables (set of executable code) that the Looper dispatches to appropriate Handlers. The MessageQueue is an unbounded LinkedList of Message objects. It inserts Messages in time order, where the lowest timestamp dispatches first.
  • A Message object basically defines a message that you can send to a Handler which in turn puts it in the MessageQueue so that later the Looper can dispatch it to its respective handler. The Message acts as a container for arbitrary data. The Message provides three pieces of extra information, required by the Handler and MessageQueue to process the message: what ( an identifier the Handler can use to distinguish messages and process them differently), time ( informs the MessageQueue when to process a Message) and target ( indicates which Handler should process the Message).
  • A Handler. Messages are not directly added to the MessageQueue. Instead, a Handler is created to take care of two things: deliver messages to the MessageQueue, and execute them as they come out of the queue. A Handler defines a set of methods using which we can post/send (as well as remove) and process Message (data message) and Runnable (task message) objects in the MessageQueue associated with the Thread-specific Looper. Each Handler instance is actually bound to the thread (and hence the message queue associated with the thread) in which it is created. It delivers Message and Runnable objects to that associated MessageQueue and executes them as they get dispatched by the Looper.
  • A Looper takes the next task, executes it, then takes the next one and so on.

A Handler allows you communicate back with the UI thread from other background thread. This is useful in Android as android doesn't allow other threads to communicate directly with UI thread. Handler can send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When a new Handler is created, it is bound to the thread/message queue of the thread that is creating it.

There are two main uses for a Handler:

  1. To schedule messages and runnables to be executed as some point in the future.
  2. To enqueue an action to be performed on a different thread than your own.
Handler is a object for sending messages between threads. Each handler is bound to a single thread, delivering messages to it and executing commands from it.

There are two ways to create Handler:

  1. Using the default constructor: new Handler().
  2. Using a parameterized constructor that takes a runnable object or callback object.

If you look at the API for Handlers now, the main methods provided make sense:

  1. post
  2. postDelayed
  3. postAtTime

A Runnable object represents a piece of code or a command that can be executed. Generally it is used to execute some command in a different Thread. This is how a Runnable object would look like:

Runnable r = new Runnable() {
    @Override
    public void run() {
        // block of code to execute
        System.out.println("Runnable run() method executed.");
    }
};

Now if you want to execute this in a separate Thread, then here’s how we can do it:

Thread t = new Thread(r);
t.start();

Similarly we could just use the Thread class:

Thread t = new Thread() {
    @Override
    public void run() {
        // block of code to execute
        System.out.println("Thread run() method executed.");
    }
};

t.start();

Example 1: sendMessage and handleMessage mechanism

A Handler in Android is a construct that can be used to communicate between UI thread and separate background threads. A Handler belongs to the thread that is creating it. For example, if you create a handler in the onCreate method of an Activity, it belongs to the UI thread, and this is usually what you want to do because you can then use this handler to post feedback to the UI thread from background threads.

TextView tvDisplay;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    tvTitle = (TextView) findViewById(R.id.tvTitle);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View view) {
            longOperation();
        }
    });
}

private void longOperation() {
    final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            tvDisplay.setText(msg.obj + "\n" + tvDisplay.getText());
        }
    };

    new Thread(new Runnable() {
        @Override
        public void run() {
            // sleep for 5 seconds
            try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}
            Message msg = new Message();
            msg.obj = "Very long operation!";
            handler.sendMessage(msg);
        }
    }).start();
}

Example 2: Using post method of the Handler

Main idea expressed below.

@Override
public void onCreate(Bundle savedInstanceState) {
    Handler handler = new Handler();

    Runnable runnable = new Runnable() {
        @Override
        public void run () {
            // do some long operation
            longOperation();

            // After runnable is done with it's job,
            // I need to tell the user that i'm done
            // which means I need to send a message back to the UI thread

             handler.post(new Runnable(){
                @Override
                public void run(){
                    // do some UI related thing
                    // like update a progress bar or TextView
                 }
            });
         }
    };

    // Cool but I've not executed the runnable yet
    // I've only defined the message to be sent
    // When I execute it though, I want it to be in a different thread
    // that was the whole point.

    new Thread(runnable).start();
}

Let's look in concrete code and appropriate layout. Following is activity_main.xml file.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:indeterminate="false"
        android:max="10"
        android:padding="4dip" >
    </ProgressBar>

    <Button
        android:id="@+id/btnStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="startProgress"
        android:text="Start" >
    </Button>
</LinearLayout>

Following is MainActivity.java file.

public class MainActivity extends Activity {
    private Handler handler;
    private ProgressBar progressBar;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler = new Handler();
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
    }

    public void startProgress(View view) {
        new Thread(new Task()).start();
    }

    class Task implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i <= 20; i++) {
                final int value = i;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        progressBar.setProgress(value);
                    }
                });
            }
        }
    }
}

Example 3: Using postDelayed method of the Handler

Following example demonstrates how to start command after 3 seconds.

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        Intent intent = new Intent(SecondActivity.this, MainActivity.class)
        startActivity(intent);
    }
}, 3000);

Here, we used the postDelayed(Runnable, time) method to send a message with a delayed time. In this case, the message is a runnable object that represents a command than can be executed.

Useful links

comments powered by Disqus