So, as the title says: What actually happens when you post a runnable from another thread onto the main thread?
I\'ve seen lots of questions asking how you do it, an
There are bunch of other runnables that the MainThread executes, such as updating the UI, touch events. The 'time' is when the posted runnable is ready to be dequeued. If any other runnable came before it, your runnable will wait.
There is no such thing here as interruption. Your button will submit burst of runnables, as same as submitting same number of runnables from many different threads.
If you have a message that is non-short (whatever contains LONG word is bad for UI) operation will block the execution of other recurrent tasks submitted in the queue, most of often demonstrated with no updates (for task that execution is indeterminate) on the UI at all or junking if it is matter of burst of runnables that execution takes longer than 8ms.
First, you need to understand what the Message
class is like. A Message
object contains among other fields the following:
Handler target; // a handler that enqueued the message
long when; // the time at which the message is to be processed
[RUNNABLE] Runnable callback; =
[SWITCHED] int what, int arg1, int arg2, Bundle data...
bool isAsynchronous; // I will talk about it in the end
What I tagged with [RUNNABLE] and [SWITCHED] represents the two non-overlapping means of processing a Message
. If the callback
is not null all the [SWITCHED] fields are ignored. If the callback
is null than the Message
is defined by [SWITCHED] fields and is processed in either the Handler's
overriden handleMessage()
or the handleMessage()
of the Handler.Callback
the handler was initialized with.
The MessageQueue
is sorted by the when
field. The Looper
will not dequeue and process a message until the current time, as measured by SystemClock.uptimeMillis
, is greater than or equal to the time stored in the message’s when
field.
When you call Handler#post(Runnable r)
the following things happen:
A Message
is obtained from the pool (a simple static linked list
in the Message
class)
Your Runnable
is assigned to the message's callback
field.
when
field is simply set to the current time if no delay or specific
time was passed
The Message
is enqueued to the MessageQueue
. If when
is
earlier than that of the head of the queue it becomes a new head. If
it's not, than it's inserted in the middle so that the MessageQueue
remains sorted by when
The Looper
which was in a non-terminating loop dequeuing the messages
from the queue and processing them in sequence (no interweaving),
eventually, dequeues our message and calls dispatchMessage()
on
the handler that originally posted the Runnable
.
The handler decides whether the message is [RUNNABLE] or
[SWITCHED] and processes it accordingly. In particular it calls
run()
on the callback
if it's present
This should answer your questions on the behavior of your Runnable
posted on the UI Thread during the blocking task - well, no, it does not interrupt the ongoing task, nor does it interweave. Everything that happens on the thread first gets into the MessageQueue
, button clicks or your custom Runnables
that you post from other threads. There is basically no way it could be happening some other way: Looper.loop()
just makes the thread busy with its for(;;)
loop.
There are ways to change the messages ordering though.
For instance, there is an interesting concept of sync-barrier in the Looper/Handler framework. A sync-barrier is by a convention just a Message
with a null target
(so it's basically just a flag-like thing, there is no handler to dispatch it). If it's put to the queue with postSyncBarrier()
, the whole process of dequeuing changes, until the sync-barrier is removed from the queue with removeSyncBarrier()
. The Messages
not marked as isAsynchronous
will be ignored and not dequeued and processed at all. Instead, the queue will be scanned until the message with isAsynchronous = true
is found. It will then be scheduled according to its when
and processed when its time comes.
Also, you can call a self-explanatory Handler#postAtFrontOfQueue()
, though, as pointed out in the documentation
This method is only for use in very special circumstances -- it can easily starve the message queue, cause ordering problems, or have other unexpected side-effects.
I suggest you browse the source code of all the classes mentioned. It reads like a good fiction book.