天天看點

Handler 如何實作定時發送消息的 handler sent 和post的差別

Handler 常用的方法

  1. Handler.postDelayed(Runnable r, long delayMillis)
  2. Handler.sendMessageDelayed(getPostMessage(r), delayMillis)
  3. Handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
  4. Handler.enqueueMessage(queue, msg, uptimeMillis)
  5. MessageQueue.enqueueMessage(msg, uptimeMillis)

那麼 handler.postDelayed 和 handler sendMessageDelayed是如何實作時間精準發送消息

handler 在發送消息後,消息會放入MessageQueue中,MessageQueue是一個簡單的單向連結清單

在插入的時候會判斷目前消息距離要執行時間和連結清單中所有消息距離要執行時間大小的比較

連結清單頭時間短,連結清單尾時間長。

然後再Looper 循環取MessageQueue中的消息的時候會判斷目前時間是否是消息可以處理的時間

如果不是,則調用nativePollOnce 進行阻塞,阻塞時間到了,再去取消息

for (;;) {
    if (nextPollTimeoutMillis != 0) {
        Binder.flushPendingCommands();
    }
 
    nativePollOnce(ptr, nextPollTimeoutMillis);
 
    synchronized (this) {
        // Try to retrieve the next message.  Return if found.
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        Message msg = mMessages;
        if (msg != null && msg.target == null) {
            // Stalled by a barrier.  Find the next asynchronous message in the queue.
            do {
                prevMsg = msg;
                msg = msg.next;
            } while (msg != null && !msg.isAsynchronous());
        }
        if (msg != null) {
            if (now < msg.when) {
                // Next message is not ready.  Set a timeout to wake up when it is ready.
                nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
            } else {
                // Got a message.
                mBlocked = false;
                if (prevMsg != null) {
                    prevMsg.next = msg.next;
                } else {
                    mMessages = msg.next;
                }
                msg.next = null;
                if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                msg.markInUse();
                return msg;
            }
        } else {
            // No more messages.
            nextPollTimeoutMillis = -1;
        }
        ...
    }
}      

handler snet 和 post 本質沒有差別都是向messageQueue中加入了消息

有差別在于回調方式的不同,post runnable 回調是回到run方法中

sent 最終會調用到handlerMessage 我們複寫的這個方法這裡

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
//獲得了message執行個體,将r賦給callback,接下來還是和sendMessage一緻的操作,進入sendMessageDelayed
 private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}      

最終還是到sendMessageAtTime這個方法裡面

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}      
/**
 * Handle system messages here.
 */
   public void dispatchMessage(Message msg) {
  //如果是post,callback不為空,直接進入handleCallback
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
   //如果是sendMessage,且建立handler時沒有傳入callback,則callback為空,直接進入handleMessage,也就是我們自己複寫的處理Message的方法
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

//直接run并不會啟動新線程,是以這就是post的runnable裡面可以直接更新UI的原因
   private static void handleCallback(Message message) {
    message.callback.run();
}