Handler機制(Looper、Message、MessageQueue)源碼追蹤

介紹

本文是自己看Handler源碼的一個流程,主要對主線進行了追蹤查看。
如有寫的不正確或者分析不正確的,麻煩大佬指出。跪謝啦!

Handler機制涉及主要相關類

  • Handler
發消息、處理消息
  • Looper
輪詢消息隊列,一個線程只有一個Looper
  • Message
消息的存儲對像
  • MessageQueue
消息列表(消息不會直接添加到MessageQueue中,
而是通過與Looper關聯的{@link Handler}對象)

先拋出幾個問題:

  1. Looper什麼時候創建的?
  2. MessageQueue什麼時候創建的?
  3. Looper和當前線程是什麼時候綁定的?
  4. Message和Handler什麼時候綁定的?
  5. Message什麼時候放入MessageQueue?

源碼追蹤

new Handler();

先從new 一個Handler看起:

點進去,看到Handler構造方法裏調用了

mLooper = Looper.myLooper();

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();// 這裏獲取一個Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback; 
        mAsynchronous = async;
    }

關鍵方法: Looper.myLooper()

myLooper();點進去發現是sThreadLocal.get()直接獲取的。

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

而sThreadLocal是一個用來存儲Looper的本地變量

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

我們再去查看sThreadLocal 哪裏調用了。
在這裏插入圖片描述發現只有三個地方調用了,有一個調用的方法是直接設置Looper的:

sThreadLocal.set(new Looper(quitAllowed)); 發現這裏直接是new Looper。

關鍵方法:sThreadLocal.set(new Looper(quitAllowed));

繼續追蹤,發現sThreadLocal.set(new Looper(quitAllowed)); 是在prepare方法裏調用的。

 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));
    }

到這裏我們發現了這是Looper的創建時間!

好奇寶寶肯定點擊進去看下的:new Looper();我們點擊看下


    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
        // 是不是發現了什麼,MessageQueue在Looper的構造方法裏創建的,同時Looper和線程也綁定了

總結: Looper的創建時間,也是MessageQueue創建的時間,同時 也是Looper和線程綁定的時間。

關鍵方法:prepare

繼續追蹤,誰又調用了prepare

prepare();

    public static void prepare() {     //   消息隊列可以quit
        prepare(true);
    }

    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));
    }

繼續追蹤,發現 Looper.class 的107行調用的prepare(false);

    public static void prepareMainLooper() {
        prepare(false);         // 這裏調用的prepare。//消息隊列不可以quit
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

再繼續追蹤prepareMainLooper ,我們會追蹤到ActivityThread這個類裏面。

prepareMainLooper()

主線程就是UI線程,Activity由ActivityThread啓動,會調用ActivityThread的main函數:


    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

		// 省略N行代碼................
        Looper.prepareMainLooper(); //  看到這裏我們就明白了,

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
		// 省略N行代碼................
        Looper.loop();// 開始輪詢

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

我們從使用Handler處追蹤,到這裏我們是往上層追蹤到ActivityThread的**main(String[]args)**方法裏。

這就證明了Looper的創建時間,Activity啓動時就會爲ui線程創建一個Looper。Looper構造方法裏進行MessageQueue的創建、Looper和線程的綁定。**

關鍵方法流程:

new Handler()  ---->  
Looper.myLooper() ----> 
sThreadLocal.set(new Looper(quitAllowed)) ---->
prepare(boolean quitAllowed)  ---->
prepareMainLooper()---->
ActivityThread的main(String[] args)

附上流程圖:
在這裏插入圖片描述

Message的創建、以及與Handler的綁定

消息創建

Message obtain = Message.obtain();也可以直接new Message ();使用obtain很更好點,看下方源碼上的註釋:

//obtain 源碼中有很多這個方法,這裏只拷貝出這兩個
/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     * 從全局池返回一個新的消息實例。讓我們
      *在很多情況下避免分配新對象
     */
 public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    //這裏也可以直接綁定Handler,即使不綁定Handler中enqueueMessage也會去綁定的 
public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

Message與Handler的綁定、Message放入MessageQueue

handler發送消息的幾個方法:

  • sendEmptyMessage()
  • sendEmptyMessageAtTime()
  • sendEmptyMessageDelayed()
  • sendMessageAtFrontOfQueue();
  • sendMessage()
  • sendMessageAtTime()
  • sendMessageDelayed

我們換個方向從這裏作爲入口去追蹤 Message與Handler的綁定:
點擊去一直往下追蹤,會發現調用哪個最終都會調用:

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;    // 這裏是Handler與Message的綁定
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);  //最終消息都會放入MessageQueue 
    }

Handler處理消息

  Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //我們從handleMessage點擊去繼續追蹤
        }
    };

消息最終由Looper取出,交給Handler的dispatchMessage進行處理

 /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
    
    /**
     * 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);
        }
    }

還記得 會調用ActivityThread的main函數嗎?調用了 Looper.loop();

  public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

		// 省略N行代碼................
        Looper.prepareMainLooper(); //  看到這裏我們就明白了,

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
		// 省略N行代碼................
        Looper.loop();// 開始輪詢

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Looper.loop();
最終由Looper取出消息進行消息分發。

  public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
         /// 省略N行代碼...........
        for (;;) {
            Message msg = queue.next(); // might block
           /// 省略N行代碼...........
            try {
                msg.target.dispatchMessage(msg);  // 這裏調用了Handler的dispatchMessage進行消息分發
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
           /// 省略N行代碼...........
        }
    }

附上消息分發流程圖:
在這裏插入圖片描述
注:如有什麼不對,理解不到位的麻煩各位看官指出!在下感激不盡。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章