Reputation: 309
for any task that had to be delayed I had been using: Handler.postDelayed(Runnable r,Long Delay)
, but I just recently came across the documentation and it says this:
Causes the Runnable r to be added to the message queue, to be run after the specified amount of time elapses. The runnable will be run on the thread to which this handler is attached. The time-base is SystemClock.uptimeMillis(). Time spent in deep sleep will add an additional delay to execution.
I have two confusions in this:
Is the delay achieved by adding the Runnable
to the MessageQueue
after a specified delay of time?
Or,
Is the Runnable
added immediately and the function is delayed for
a specified time. If this is so there must be mechanism that the
Runnable is dequeued and put somewhere else so that other items in
the queue can be processed. How is the Runnable delayed and how is
the MessageQueue
maintained during this delay?
Upvotes: 1
Views: 2259
Reputation: 3147
The structure we call Handler
runs with a Looper
which contains a MessageQueue
inside it. A call to Handler.post(runnable)
creates a new message for MessageQueue
and puts it at the last of the queue, where its time is set as SystemClock.uptimeMillis()
. Looper
always loops for checking messages and if there is a message and the target time has been passed, it executes the Runnable
inside it.
To clarify:
Handler.post(runnable)
sends a message with SystemClock.uptimeMillis()
where on the next check, the new SystemClock.uptimeMillis()
becomes bigger than the message's execution time, allowing the Looper
to fetch the runnable and execute it.
Now, if you use Handler.postDelayed(runnable, delay)
it posts the message with SystemClock.uptimeMillis() + delay
where the delay is in milliseconds, which we will call messageTime
for now. What does the looper do? It checks the message, if SystemClock.uptimeMillis()
is smaller than messageTime
, it skips the execution. Looper always loops, so there becomes a time where SystemClock.uptimeMillis()
actually becomes bigger than messageTime
where the runnable becomes eligible for execution.
Unless you call Handler.postAtFrontOfQueue(runnable)
, the calls are always put to the bottom of the queue, not the top.
Finalize:
Handler.post(runnable)
--> messageTime = SystemClock.uptimeMillis()
Handler.postDelayed(runnable, delay)
--> messageTime = SystemClock.uptimeMillis() + delay
.
Looper executes the runnables where SystemClock.uptimeMillis() >= messageTime
. That's the gist of it.
Upvotes: 2
Reputation: 2121
runnable is immediately added to the message queue which can be seen in source code of the handler class postDelayed->sendMessageDelayed->sendMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
queue.enqueueMessage
runs a for (;;)
to delay the callback.
In this continuous loop message's time is checked for execution. you can see this in the source code
Upvotes: 2