添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Runnable 介紹

Runnable 基本上也是 Message, 當調用 postDelayed() 調度某個 Runnable 的時候, 會將其包裝成一個 Message,然後再使用 sendMessageDelayed()這個方法進行發送 。不論是 post() 還是 send() 這類的方法,他們最後都是經過 Handler 的 sendMessageAtTime() 方法來加入到 MessageQueue , 最終經 由 發送他們的 Handler 做處理。

Handler 的源碼 , 當 Handler 透過 postDelayed() 調度某個 Runnable , 其實還是被包裝成 Message 經由 sendMessageDelayed 這個方法進行發送

public final boolean postDelayed(@NonNull Runnable r, long delayMillis) { return sendMessageDelayed(getPostMessage(r), delayMillis);

如下:由下我們可以得知 , 不論是 sendEmptyMessage , sendEmptyMessageDelayed 抑或是sendMessageDelayed , 最終都會導到 sendMessageAtTime() 方法來將 Message 加入到 MessageQueue

public final boolean sendEmptyMessage(int what) return sendEmptyMessageDelayed(what, 0); public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); public final boolean sendMessageDelayed(Message msg, long delayMillis) if (delayMillis < 0) { delayMillis = 0; return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

sendMessageAtTime() 方法 會執行 enqueueMessage()

public boolean sendMessageAtTime(@NonNull 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);

Handler 是如何處理自己發送的訊息的呢?

上一篇有提到 Message 會關聯 送出他的 Handler , 當 輪到這個 Message , Looper會把它取出 , 送出它的 Handler 會把它處理掉。looper 拿到 Message 後 透過 msg.target 找到了當初送出他的 Handler ,這 Handler 會利用 dispatchMessage 將 Message 處理掉。

Handler 處理訊息的路徑有三

  • 如果這個 message 本身帶有 callback , Runnable 處理 即是走這條
  • 如果我們有傳 callback 給 Handler ,會是走這條 ,昨天的實作即是走這條路徑
  • 以上兩者就非 , 我們自己寫一個 Handler class ,覆寫 Handler 內的 handleMessage 方法
  • * Handle system messages here. public void dispatchMessage(Message msg) { // 1. if (msg.callback != null) { handleCallback(msg); } else { // 2. if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; // 3. handleMessage(msg);

    handleCallback 方法

    private static void handleCallback(Message message) { message.callback.run();

    Handler 內的 handleMessage 方法

    public void handleMessage(Message msg) {
    

    實作 Handler

    今天只要實例化一個 Handler() , 我們會走上述第二條路 , 不傳 callback 給 Handler

    handler = Handler()

    實作 Runnable

    dispatchMessage() 方法中 在處理 第二條路的 Message , 會調用 handleCallback ()
    Runnable 的 run 方法會得到 回調。 在 run 方法 執行我們想執行的程式碼。

    下面在 run() 方法內 , handler.postDelayed(runnable,1000) 這一行 , 我們又送出了 Message , 這行
    是今天實作的重點。在處理完Message之後 又再送出 Message , 此時會變成一個循環。

    runnable = object :Runnable{
            override fun run() {
                currentms++
                setCurrentTime()
                handler.postDelayed(runnable,1000)
    

    postDelayed 方法 需傳入的兩個參數 第一個 是這個 Runnable , 第二個是 Delay 幾秒才送出訊息

    Handler 送出 Message

    Handler 透過 postDelayed 將 Runnable 包裝成 Message 送出 , 開始計時。
    Runnable 的 run 方法會獲得 回調 時 , Handler 在調用 同樣的方法 ,送出 Message 。

    handler.postDelayed(runnable,1000)

    Handler 移除 Message

    當我們按下 停止按鈕時 , 調用 removeCallbacks()去移除 MessageQueue 上 runnable 包裝成的 Message , 此時 run 方法不再獲得回調 , 停止計時

    handler.removeCallbacks(runnable)

    為何要 Delay ? 不直接post () 送出就好

    今天 實作的 Timer 透過 postDelayed , 延遲 1 s 才執行 增加一秒 , 如果 post () 就沒有時間間隔了

    實作 Timer 的原理

    Timer 透過 postDelayed , 過了 1 s 之後 , 變數自動 加 1 , 這樣實作 出計時的功能

    currentms++

    今天的源碼

    https://github.com/liyiwin/Day4_Timer