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:
Some famous Android constructs you’ve probably come across:
Let's look at bunch of Handler
, Message
, MessageQueue
and Looper
.
Each main thread comes with following objects:
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 Handler
s. The MessageQueue
is an unbounded LinkedList
of Message
objects. It inserts Messages
in time order, where the lowest timestamp dispatches first.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).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
.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
:
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
:
new Handler()
.If you look at the API for Handlers now, the main methods provided make sense:
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