0% found this document useful (0 votes)
7 views

Java Processes and Threads Related Concepts

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views

Java Processes and Threads Related Concepts

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

Understanding Android/Java Processes and

Threads Related Concepts (Handlers,


Runnables, Loopers, MessageQueue,
HandlerThread)
3 Replies

In this article we’ll try to briefly go through the various sort of low level concepts in Android
that are really important to understand IMHO. Once you have a good grasp on these, a lot of
things that are actually built atop these concepts become much easier to understand and code.
We’ll go through processes, threads, loopers, message queues, messages, handlers, runnables,
etc. I’ll also point to various external resources that you should definitely go through for a much
better understanding.

Process
When an Android application starts for the first time, the Android system starts a new Linux
process for the application with a single thread of execution (also known as the main thread or
UI thread). By default all the components of an application runs in the same thread (“main”
thread) of that process.

By default every Android application will run in its own process and will be assigned its own
unique User ID that can be set by the sharedUserId manifest attribute. Two applications could
share the same user ID (if they’re also signed by the same certificate) in which case they can read
each others data even when running in separate processes. You can always go to $ adb shell
and execute $ ps to view a list of processes on your Android device or emulator connected to
your computer.

Each process has its own memory space and communicate with each other through various Inter
Process Communication (IPC) mechanisms like pipes and sockets not only on the same systems,
but also different systems.

Helpful Resources:

 http://developer.android.com/guide/components/processes-and-threads.html

Thread
Threads are the smallest sequence of programmed instructions that can be managed
independently by operating system (schedulers). They are units of computation that run
simultaneously within a process (Linux process in Android). They usually reside in a process and
share resources such as memory (but have independent call stacks). Threads running in the same
process can communicate with each other via shared objects or message passing. Creating
threads require fewer resources and they’re also sometimes referred to as lightweight processes.

Concurrent Programming (Concurrency) is basically when processes and threads execute


simultaneously in multiple processors (CPU) or multiple cores on a system (computers, mobiles,
etc.).

In Android, when an application is launched, the system creates a thread of execution called
“main”. This main thread (also called the UI thread) is responsible for drawing the user interface
including dispatching events to appropriate user interface widgets, toolkits and other
components. It is the thread responsible for what the user gets the see on the screen and interacts
with.

All the components (Activity, Service, BroadcastReceiver, Content Providers) instantiated in the
same process/application are not created in a separate thread but the main thread only. So for
example:

 Start of an Activity
 Execution of a Service
 Receival of BroadcastReceiver (onReceive())
 Querying a content provider
 Responding to system callbacks for user events like onTouchListener, onKeyDown

All these and more, all are processed on the main thread. This is why long running operations
like network access, database queries, complicated calculations, accessing big files or any task
that could possibly take more than 5 seconds should not be executed on the main thread which’ll
cause it to block leading to no events getting dispatched, including drawing events. The user will
also be presented with the Application Not Responding (ANR) dialog leading to an unresponsive
laggy application. This could lead to users uninstalling your app and submitting low ratings and
negative feedbacks on the play store.

Hence, all sorts of intensive operations should be done in Background/Worker threads. It is


recommended to user Services (has no user interface like an Activity) for long-running
background operations by spawning a separate worker thread in it.

More Helpful Resources:

 http://developer.android.com/guide/components/processes-and-threads.html
 https://docs.oracle.com/javase/tutorial/essential/concurrency/procthread.html
 http://stackoverflow.com/questions/200469
 http://stackoverflow.com/questions/1762418
 http://stackoverflow.com/q/11566780
 http://stackoverflow.com/questions/11909248
 http://stackoverflow.com/questions/3318750
 http://www.uml-diagrams.org/examples/java-6-thread-state-machine-diagram-
example.html

Looper and MessageQueue


Android maintains a MessageQueue (message loop) on the main thread which basically contains
a list of Messages or Runnables (set of executable code) that the Looper dispatches to
appropriate Handlers.

Messages are not added to the MessageQueue directly in Android, but through the Handler
objects from any thread (but the same process) associated with the Looper. So the Looper sort of
polls (also referred to as a “tick”) for the next message in the queue and as soon as a message is
encountered, it is passed to its respective handler. You might want to go through the
Looper.loop() source code where you’ll notice an infinite for loop being run checking for the
next message. If I’m not mistaken every message has a reference to the next message too.

So basically a Looper contains a synchronized MessageQueue that’s used to process Messages


placed on the queue. It implements a Thread-specific event loop that waits for and dispatches
Messages to handlers. By default, a thread does not have a message loop associated with it,
hence doesn’t have a Looper either. To create a Looper for a thread and dedicate that thread to
process messages serially from a message loop, you can use the Looper class.

This is how you make a thread capable of having a Looper:

1
2 class LooperThread extends Thread {
3 public Handler mHandler;
4
5 @Override
6 public void run() {
7 // Initialize the current thread as a Looper
// (this thread can have a MessageQueue now)
8 Looper.prepare();
9
10 mHandler = new Handler() {
11 @Override
12 public void handleMessage(Message msg) {
// process incoming messages here
13 }
14 };
15
16 // Run the message queue in this thread
17 Looper.loop();
18 }
}
19
20

Next, starting the thread is as simple as doing this:


1// Let's say from MainActivity.onCreate()
2
3LooperThread looperThread = new LooperThread();
4looperThread.start();

Since you’ve a reference to the Handler object inside the Looper, you can send Messages or post
Runnables. We’ll discuss how to do this later.

Looper.loop() is a blocking method ensuring that the run() method blocks and is not finished.

A thread can have only one associated Looper and hence a single message queue. So if multiple
threads tries to send messages to a particular Looper thread then all of them will be processed
sequentially. Trying to setting up another Looper will throw a runtime error with a message like
this java.lang.RuntimeException: Only one Looper may be created per thread.

There are 2 methods to terminate a Looper:

 Looper.quit() – Terminates the Looper without processing any more messages in the
MessageQueue. Any attempt to post messages to the queue will fail once the Looper is
asked to quit. For example, the Handler.sendMessage() or Handler.post()
dispatching methods will return false.
 Looper.quitSafely() – Similar to the previous version but makes sure that the pending
messages that are due to be delivered are handled. Messages with due times in the future
won’t be delivered before the Looper quits though.

Terminating the Looper lets the thread resume running the method that had invoked the call to
Looper.loop(). The old Looper or even a new Looper cannot be started anymore. So the thread
can no longer enqueue and handle messages. Looper.prepare() called again should throw a
RuntimeException whereas Looper.loop() called again will block but no messages will be
dispatched from the queue.

In the previous code sample where we created a Looper thread, you shouldn’t forget to terminate
the Looper when its purpose is fulfilled. Termination could be for instance done in the Activity’s
onDestroy() method. The Activity that kicks off the thread.

UI Thread Looper

The UI thread is the only thread that is associated with a Looper by default before the application
components are initialized. There are a few differences (or maybe specialities that it has)
between it and other application thread Loopers:

 It cannot be terminated with Looper.quit(). A RuntimeException is thrown if that is


tried.
 It is accessible from everywhere through the Looper.getMainLooper() method.
 It is associated with the UI thread via Looper.prepareMainLooper() and can be done
only once per application. Trying to call this in some other Thread to attach the main
looper will throw an exception.

MessageQueue.IdleHandler

Pending Messages are sorted based on timestamps in the MessageQueue. The one with the
lowest value and less than the current time is the first one to be dispatched. The dispatcher waits
(blocking the Looper thread) until the current time has passed the lowest timestamp value from
the queue. If there’s a new message with the lowest timestamp then the list is rearranged for
proper sorting so that this recent message can be dispatched first.

If there’s no message to process then that means the consumer thread has some idle time that it
can use to execute some task rather than just waiting. Infact this way your idle slots can be filled
with execution of short non-critical tasks. Make sure you don’t perform any long running
operations that might delay the dispatch of pending Messages. So in these idle slots the
queueIdle() method on all the registered MessageQueue.IdleHandler interfaces are called.
Let’s see an example:

1
2 // Get the MessageQueue object associated with the current thread
3 MessageQueue mq = Looper.myQueue();
4
5 // Create an IdleHandler
MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler() {
6 @Override
7 public boolean queueIdle() {
8 // ... Do some jazz
9 return false;
10 }
};
11
12// Register the IdleHandler
13mq.addIdleHandler(idleHandler);
14
15// Unregister an IdleHandler (if you want to)
16mq.removeIdleHandler(idleHandler);
17

So queueIdle() will be called when the message queue has run out of messages and has to wait
for more. If you return true from the method then your handler will be active and keep on
getting called for further successive idle time slots. If false is returned then it becomes inactive
and gets removed (same as calling removeIdleHandler()).

Terminating Unused Thread with IdleHandler

In the queueIdle() callback, you can somehow get the Looper and quit() it causing the
associated Thread to terminate if you want to. This way if you’re sure that your producer thread
inserts Messages without any delay and the consumer thread is never idle until the last message
dispatched then you can terminate the thread causing it to free up resources/memory. Remember
idle slots can occur before the first message, between messages and after the last message. So if
you terminate betwen messages then that could be a problem. Be careful!

1// Various ways to quit


2Looper.getLooper().quit(); // Cannot do this on main thread
3// or
4mHandler.getLooper().quit(); // from a Handler

Quitting from a Looper makes it return from Looper.loop(). Assuming you don’t have any
code following that in the run() method of the Thread, the thread should definitely quit (but it
really depends upon the implementation).

Helpful Resources:

 http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/ (with regards


to JS but useful and conceptually similar)
 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop (with
regards to JS but useful and conceptually similar)
 https://www.youtube.com/watch?v=A0PAhoHzlsQ&list=PLonJJ3BVjZW6hmkEaYIvLL
m5IEGM0kpwU&index=4 (Very simple Illustrations-based explanation of Looper,
MessageQueue, Messages and Threads)

Runnable
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:

1
Runnable r = new Runnable() {
2 @Override
3 public void run() {
4 // Block of code to execute
5 System.out.println("Runnable run() method executed.");
}
6};
7

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

1Thread t = new Thread(r);


2t.start();

Similarly we could just use the Thread class:

1Thread t = new Thread() {


2 @Override
3 public void run() {
// Block of code to execute
4 System.out.println("Thread run() method executed.");
5 }
6};
7
8t.start();
9

Here’s an interesting SO thread that talks about both the versions where in one we use the
Runnable interface and in the other one the Thread class. It’s worth reading.

If we wanted to execute the Runnable piece in the UI thread then we could have used the
Activity.runOnUiThread() method. In that case if the current thread is the UI thread then the
runnable will be executed immediately else it’ll be put on to the MessageQueue of the UI thread.
Another way to do this same thing is like this:

1new Handler(Looper.getMainLooper()).post(runnable);

Note: The View class has a post() method too, which lets you add a Runnable to the UI thread’s
MessageQueue. Hence, the runnable will be run on the user interface thread. You can read this
SO thread to understand the difference between Activity.runOnUiThread() and View.post().

There’s a general notion that Runnables mean they’ll be executed in a different thread which is
not through unless you explicitly wrap it in the Thread class or maybe use it in conjunction with
a Handler.

Message
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. We’ll
discuss how to send and receive Messages using Handlers in the next section. Using Handlers of
UI thread in another thread we can pass on Messages from the worker thread to the UI Handler
which will place it in the MessageQueue for later dispatching. This helps with inter-thread
communication.

So generally creating a Message is done via either Message.obtain() or


Handler.obtainMessage(). There are various versions of both the methods that accepts various
arguments. Once called, these static methods will return a Message object from a global pool of
recycled objects (better performance).

Let’s see an example of creating a Message:

1 Message msg = Message.obtain();


2
3 // Read the documentation on the properties used below
4 // http://developer.android.com/reference/android/os/Message.html
5
6 msg.arg1 = 100;
msg.arg2 = 200;
7
8 msg.obj = "String";
9
10Bundle bundle = new Bundle();
11bundle.putString("foo", "bar");
12msg.setData(bundle);
13
14// At a later stage
// handler.sendMessage(msg);
15
16

Handlers
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 Handler has to be bound to a Looper else they cannot function. A Looper is strictly required
for it to couple with/connect to a queue to insert messages and receive messages from the
Looper. When creating a Handler object you’ve to pass the Looper object as the first argument to
the constructor explicitly. If not then it binds to the Looper of the current thread. If you try to be
implicit and the thread doesn’t have a Looper then a RuntimeException will be thrown.

Note: Multiple Handlers doesn’t ensure concurrent execution as the Messages/Runnables will be
posted in the queue from which they’re processed sequentially.

Let’s see how to enqueue Messages to the MessageQueue associated with the Handler and later
when it’s dequeued the Handler itself will receive and handle/process it:

1 Handler mHandler;
2
3 class MyThread implements Runnable {
4
5 @Override
public void run() {
6 Message msg = Message.obtain();
7 msg.obj = "My message!";
8
9 mHandler.sendMessage(msg);
10 }
11}
12
13@Override
14protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
15 setContentView(R.layout.activity_main);
16
17 mHandler = new Handler() {
18 @Override
19 public void handleMessage(Message msg) {
System.out.println(msg.obj);
20 }
21 };
22
23 Thread t = new Thread(new MyThread());
24 t.start();
25 }
26
27
28

The Handler was created in the UI thread, hence it’s associated with it and its MessageQueue. In
a Runnable that is executed in a completely different Thread, a Message is created using
sendMessage() and passed on to the Handler’s MessageQueue which is the one associated with
the UI thread. Once the event loop iterating the queue gets to that Message, it passed it on to the
handleMessage() method which executes in the main thread’s context. This behaviour of
handleMessage() running in the UI thread’s context means you won’t have to use
runOnUiThread() if you’ve to do some really important operation like modifying the UI by
manipulating the Views in the layout. This way you’ve also seen how Handlers can be used to
communicate between Threads.

There’s this version of Message.obtain(Handler h, Runnable callback) that accepts the Handler
object and a Runnable callback. Apparently when you do a msg.sendToTarget() or
handler.sendMessage(msg), they won’t be sent to Handler.receiveMessage() but just the
Runnable callback will get executed (but without the Message object). It is actually somewhat
similar to doing this handler.post(runnable).

Once the Message is processed, its state is cleared and the instance is returned to the message
pool by the runtime (VM) so that it can be recycled later.

Let’s also see how we can enqueue a Runnable to the MessageQueue using Handlers:

Runnable r = new Runnable() {


1 @Override
2 public void run() {
3 System.out.println("My Runnable");
4 }
5};
6
Handler handler = new Handler();
7handler.post(r);
8
9

Pretty simple! The Handler posts the Runnable in the MessageQueue and once the Looper gets to
it, the Runnable is executed. You should go through all the methods available in the Handler
documentation. For example there’s postDelayed() and sendMessageDelayed() using which
you can schedule the processing of Runnable and Message objects to a later time.

The dispatchMessage() method of the Handler class is used to dispatch messages to the
appropriate consumer thread by the Looper. If you try to directly call the method then the
message will be processed immediately on the calling thread and not the consumer thread (that
might be UI thread in your case). Here’s a SO thread that has some relevant information.

A very important thing to remember is that only Message objects are allowed in the
MessageQueue. Hence Runnables sent through the post* methods and what integers sent
through sendEmptyMessage* are wrapped into Message objects.

Remove Messages from the MessageQueue

Once you’ve enqueued messages, it is possible to remove those from the queue that have not
been dequeued by the Looper, i.e., the pending ones. The Handler class gives us various methods
to do so.

If you want to remove the Runnables that you posted, then we have:

 removeCallbacks(Runnable r)
 removeCallbacks(Runnable r, Object token)

For removing messages from the queue, we have:

 removeMessages(int what)
 removeMessages(int what, Object object)

Oh and if you want to remove both Runnables (tasks) and Messages (data) from the message
queue, then we have this:

 removeCallbacksAndMessages(Object token)

If you pass null to removeCallbacksAndMessages(), then that’ll remove all the callbacks and
messages.

Let’s quickly see an example of how removal is done:

1 Object token = new Object();


2 Handler handler = new Handler() {
3 @Override
4 public void handleMessage(Message msg) {
5 // Do something with the msg
}
6 };
7
8 // Add a Message
9 Message msg = handler.obtainMessage(0, token);
10handler.sendMessage(msg);
11
12// Add a Runnable
handler.postAtTime(new Runnable() {
13 @Override
14 public void run() {
15 // Do some task
16 }
}, token, SystemClock.uptimeMillis());
17
18
19// and the removal!
20handler.removeCallbacksAndMessages(token);
21
22
23
24

Remember only pending messages can/will be removed. I don’t think there’s a neat way (API) to
find out whether a message has been dispatched and called without some hacks.

Note: A Handler cannot remove messages that were inserted by another Handler in the queue.

Observing (Dump or Track) the MessageQueue

It is possible to either dump the entire list of pending messages from the MessageQueue or
observe the messages as they get dispatched.

1mHandler.dump(new LogPrinter(Log.DEBUG, TAG), "");

Using the Handler.dump() instance method you can print a snapshot of all the pending
messages.

Similarly you can trace the dispatching of messages by the Looper associated with the message
queue of the calling thread like this:

1Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, TAG));

Using the setMessageLogging() on your Looper object you can print information at the
beginning and end of each message dispatch identifying the Handler and message contents.

HandlerThread
HandlerThread is a handy class for starting a new thread that has a Looper (hence an associated
MessageQueue) attached so that one doesn’t have to go through creating a Thread and calling
Looper.prepare(), Looper.loop(), etc. in that. You generally need a thread attached with a
Looper when you want sequential execution of tasks without race conditions and keep a thread
alive even after a particular task is completed so that it can be reused so that you don’t have to
create new thread instances. Also starting the same thread object again raises
IllegalThreadStateException with a Thread already started message.

Once a HandlerThread is started, it sets up queuing through a Looper and MessageQueue and
waits for incoming messages to process:

1
2 HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
3
4 // Create a handler attached to the HandlerThread's Looper
5 mHandler = new Handler(handlerThread.getLooper()) {
6 @Override
7 public void handleMessage(Message msg) {
8 // Process messages here
}
9 };
10
11// Now send messages using mHandler.sendMessage()
12

There’s only one associated MessageQueue on the thread, hence execution is guaranteed to be
sequential and therefore thread-safe. Behind the scenes, HandlerThread guarantees no race
condition between the Looper creation and sending messages by making
handlerThread.getLooper() a blocking call until the it is ready to receive messages. This is an
important reason why HandlerThread should be used over manual setup with
Looper.prepare(), Looper.loop(), Looper.quit(), etc.

The HandlerThread.onLooperPrepared() method can be used to execute some sort of setup


before the Looper loops, like creating a Handler that will be associated with the
HandlerThread. This method is invoked on the background thread when the Looper is prepared
(after the Looper.prepare() call).

If you want to prevent access to the Handler that is used to pass a data message or a task to a
HandlerThread and ensure that the Looper is also not accessible, then you can create a separate
class with a private Handler and public methods that actually does the job of passing messages
or tasks. Something like this:

1 class MyHandlerThread extends HandlerThread {


2
private Handler mHandler;
3
4 public MyHandlerThread() {
5 super("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
6 }
7
8 @Override
protected void onLooperPrepared() {
9 super.onLooperPrepared();
10
11 mHandler = new Handler(getLooper()) {
12 @Override
13 public void handleMessage(Message msg) {
switch (msg.what) {
14 case 1:
15 // Handle message
16 break;
17 case 2:
18 // Handle message
break;
19 }
20 }
21 };
22 }
23
public void taskOne() {
24 mHandler.sendEmptyMessage(1);
25 }
26
27 public void taskTwo() {
28 mHandler.sendEmptyMessage(2);
29 }
30
31}
32
33
34
35
36

Pretty straightforward code. Although one important line of code to notice is this –
super("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);. This
HandlerThread constructor takes a:

 name argument required for debugging purposes so that the thread can be found easily in
logs.
 priority argument specified via Process.setThreadPriority() with values supplied
from Process. The default priority is Process.THREAD_PRIORITY_DEFAULT (same as
that of the UI thread) but can be lowered down to
Process.THREAD_PRIORITY_BACKGROUND (less important tasks).

You can go through the HandlerThread source code here.

Note: It’s a good idea to quit the HandlerThread when you don’t need it anymore.

You might also like