mathmaniage
mathmaniage

Reputation: 309

android- handler.postDelayed how does it achieve the Delay

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:

  1. Is the delay achieved by adding the Runnable to the MessageQueue after a specified delay of time?

    Or,

  2. 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

Answers (2)

Furkan Yurdakul
Furkan Yurdakul

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

a0x2
a0x2

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

Related Questions