Handler源碼解析系列一

版權(quán)聲明

版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處+地址

什么是Handler消息機(jī)制

Android內(nèi)部的Handler消息機(jī)制,是將子線程處理的結(jié)果,通過Handler異步回調(diào)給主線程,讓主線程去進(jìn)行操作的一個(gè)機(jī)制。

Handler消息機(jī)制的工作流程圖

Handler調(diào)用流程圖.png

Handler的源碼解析

Handler使用案例

首先,給大家簡單的展示下Handler的使用流程代碼

/**
 * Function:簡述Handler使用方式
 *
 * @author wanzi Created on 2019/4/28
 */
public class HandlerActivity extends AppCompatActivity {
    private MyHandler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mHandler = new MyHandler();
        Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
            @Override
            public void run() {
                Message msg = new Message();
                msg.what = 1;
                mHandler.sendMessage(msg);
            }
        }, 5000, TimeUnit.MILLISECONDS);
    }

    private static class MyHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                Log.d("HandlerTestActivity", "Handler發(fā)來賀電");
            }
        }
    }
}

Handler是如何將消息加入到消息隊(duì)列?

分析源碼,我們首先要找到切入點(diǎn),mHandler.sendMessage(msg),代碼從這個(gè)函數(shù)開始調(diào)用的handler,那么繼續(xù)往下追蹤,我們來研究下,在發(fā)送消息之后,Handler干了什么?
Handler.java

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
  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);
    }

在經(jīng)過層層調(diào)用之后,我們發(fā)現(xiàn)調(diào)用到了enqueueMessage方法,根據(jù)字面意思,就是加入消息隊(duì)列,那我們繼續(xù)看里面的實(shí)現(xiàn)
Handler.java

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
  • 將當(dāng)前的handler對(duì)象賦值給了msg.target
  • 調(diào)用了messagequeue的enqueueMessage方法
    MessageQueue.class
 boolean enqueueMessage(Message msg, long when) {
       ...

        synchronized (this) {
         ...
            if (p == null || when == 0 || when < p.when) {
           ...
            } else {
                ...
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

          ...
        }
        return true;
    }

到此為止,msg算是終于成功的加入到消息隊(duì)列當(dāng)中,當(dāng)然MessageQueue.enqueueMessage的內(nèi)容遠(yuǎn)不止這么簡單,里面包含一些異常處理,喚醒機(jī)制等等,單獨(dú)寫一篇文章詳細(xì)的講解。

Handler如何從消息隊(duì)列獲取消息?

主線程的Looper如何創(chuàng)建?

說到獲取消息,我們還得從Looper說起,大家都知道,在主線程中創(chuàng)建的Handler對(duì)象,是自帶Looper對(duì)象的,那么主線程中的Looper是從哪里創(chuàng)建的呢?
我們知道,程序啟動(dòng)開始的入口在ActivityThread.class的main函數(shù),我們來看看代碼
ActivityThread.class

public static void main(String[] args) {
      ...
        Looper.prepareMainLooper();

       ...
        Looper.loop();

        ...
    }

簡化之后,我們可以看出,在main函數(shù)中,有兩處和Looper相關(guān)的代碼,那么我們分別來看下
** Looper.class**

 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
  • prepareMainLooper最終調(diào)用到的是prepare函數(shù)
  • prepare的quitAllowed參數(shù)值為false,意味著在主線程中,不允許退出
  • 在prepare函數(shù)中,我們new了一個(gè)Looper對(duì)象,放在了一個(gè)ThreadLocal的對(duì)象中保存
  • sMainLooper 是將sThreadLocal中保存的值取出,賦值給它

ThreadLocal的簡單介紹

在整個(gè)Looper的創(chuàng)建以及獲取的過程,其實(shí)主要跟ThreadLocal的set,get函數(shù)相關(guān),那么我們就來看看這兩個(gè)函數(shù)的源碼

 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
  • set函數(shù)雖然只需要傳value值進(jìn)來,但是其實(shí)它是獲取的當(dāng)前線程作為key值,將其保存,這也就說明為什么一個(gè)線程只對(duì)應(yīng)來一個(gè)looper,因?yàn)樗拇鎯?chǔ)的數(shù)據(jù)結(jié)構(gòu)只支持一對(duì)一的關(guān)系。
  • get函數(shù)也是通過以當(dāng)前線程作為key將對(duì)應(yīng)的entry取出,返回給調(diào)用方

那么以上就是mainLooper的創(chuàng)建的主要流程

主線程的Looper如何開啟消息輪詢的呢?

大家回顧下前面ActivityThread.main方法,我們還有一行代碼沒有分析,那就是Looper.loop,根據(jù)字面意思我們也能猜出,這是開始進(jìn)行循環(huán),話不多說,上代碼
Looper.class

 public static void loop() {
        final Looper me = myLooper();
    ...
        final MessageQueue queue = me.mQueue;
    ...
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
    ...
    ...
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
          ...
    }

這里我們同樣省略了很多源碼,只分析主要流程

  • 通過myLooper()獲取到當(dāng)前線程的looper對(duì)象,上面也帶大家一起分析過源碼了,有不清楚的可以回過去看看
  • 通過拿到的looper對(duì)象去獲取到當(dāng)前的消息隊(duì)列
  • 進(jìn)入死循環(huán)
    • 不斷的從隊(duì)列中獲取新的消息
    • 獲取到消息后,msg.target.dispatchMessage(msg)

以上就是Looper開啟輪詢的整個(gè)調(diào)用流程。

下面我?guī)Т蠹乙黄鹂聪聨讉€(gè)問題:

  1. MessageQueue.next是如何拿到message的呢?
    MessgeQueue.class
  Message next() {
      ...
        for (;;) {
           ...
            synchronized (this) {
              ...
                Message prevMsg = null;
                Message msg = mMessages;
                ...
                if (msg != null) {
                    if (now < msg.when) {
                       ...
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                       ...
                        msg.markInUse();
                        return msg;
                    }
                } else {
                  ...
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

             ...
    }
  • 在next函數(shù)中也有一個(gè)死循環(huán),當(dāng)獲取到我們的msg之后,跳出循環(huán)
  • next函數(shù),就是從當(dāng)前的message獲取到nextMessage,并將其返回給looper

看完之后,你一定還有很多疑問,為什么要用到死循環(huán)?當(dāng)消息隊(duì)列為空的時(shí)候,next函數(shù)會(huì)怎么辦?等等等等,跟MessageQueue.enqueueMessgae方法一樣,next函數(shù)的實(shí)現(xiàn)大有文章,單獨(dú)為大家寫一篇詳細(xì)介紹,這里只作簡單的流程分析。

  1. 什么情況下msg為空,跳出整個(gè)循環(huán)呢?
    這一塊先跟大家簡單的介紹一下,在next函數(shù)中,我們發(fā)現(xiàn)有return 為 null的情況,取決于一個(gè)叫mQuitting的參數(shù),其實(shí)就是調(diào)用了MessageQueue.quit()方法,將其設(shè)置為true。但是在主線程中,大家還記得我們?cè)贚ooper.prepare傳入的false參數(shù)嗎?它不允許主線程的MessageQueue執(zhí)行quit方法,直接拋出異常。
 void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }
  1. msg.target.dispatchMessage(msg)是怎么回調(diào)到handler.handleMessage方法的呢
    ** Message.class **
   /*package*/ Handler target;

是的,正如上面我們所預(yù)料的,target其實(shí)是一個(gè)handler的對(duì)象,在Handler.enqueueMessage方法中,我們將msg與handler進(jìn)行了關(guān)聯(lián),那么在Looper.loop里面,msg.target.dispatchMessage(msg),根據(jù)源碼可以得知,我們將其回調(diào)給了handleMessage進(jìn)行事件處理。

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

以上就是針對(duì)Handler源碼的一個(gè)簡單的分析,后續(xù)會(huì)對(duì)一些細(xì)節(jié)化的設(shè)計(jì),進(jìn)行專題講解,敬請(qǐng)關(guān)注。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容