1. 程式人生 > >Handler訊息機制原始碼分析

Handler訊息機制原始碼分析

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

先來個Handler執行過程的總結:


    1、 Looper.prepare()方法
        為當前執行緒繫結looper,
        在looper構造方法中建立一個messageQueue

    2、 建立handler 重並寫handleMessage方法

    3、 使用handler傳送訊息,最終訊息都會發送至messageQueue物件中,在messageQueue當中,所有的message按應該執行的時間的先後順序,從小到大排列



    4、Looper.loop()
        在此方法中,開啟死迴圈,不斷的從messageQueue中取出應該執行的message,並執行message 對應的handler中的dispatchMessage方法,即,執行我們重寫的handleMessage方法

參照以上分析在子執行緒中建立Handler物件:

	new Thread(){
	    @Override
	    public void run() {
		Message msg = Message.obtain();
		Looper.prepare();//若沒有呼叫此方法則丟擲異常 Can't create handler inside thread that has not called Looper.prepare()
		Handler handler2 = new Handler(){
		    public void handleMessage(Message msg) {
			Toast.makeText(MainActivity.this, "收到子執行緒message訊息", 0).show();
		    };
		};
		handler2.sendMessage(msg);
		Looper.loop();
	    }
	}.start();
對比在主執行緒中建立Handler例項物件我們發現,在子執行緒中建立Handler物件需要在建立前呼叫Looper.prepare()方法在建立後呼叫Looper.loop方法,那究竟這兩個方法是做什麼的呢?

先看看系統的Looper.prepare方法:

public static final void prepare() {

        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
		// 為當前執行緒繫結一個looper物件,以sThreadLocal為key
        sThreadLocal.set(new Looper());
    }
即:呼叫Looper.prepare方法時為當前執行緒綁定了一個Looper物件,所以Looper.prepare方法只能呼叫一次,即一個執行緒只能有一個Looper物件

再看看Looper的構造方法:

 private Looper() {
        mQueue = new MessageQueue();
    }
因為一個執行緒只能有一個Looper物件,所以一個執行緒也只能有一個MessageQueue物件

先讓我們看看Handler的構造方法:

public Handler() {
	//獲得當前執行緒的looper物件
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
		// 獲得looper中MessageQueue的引用
        mQueue = mLooper.mQueue;
    }

再看看系統的Looper.myLooper方法:即獲取呼叫Looper.prepare方法時儲存在sThreadLoad的Looper物件,所以Looper.prepare方法要在new Handler方法前呼叫

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

即:當建立Handler時會先呼叫Looper.myLooper()方法獲取當前執行緒的Looper物件,如果Looper==null,則丟擲異常

通過以上兩個方法,當前執行緒的唯一Looper物件和MessageQueue物件都已建立,接下來該sendMessage了

檢視系統原始碼可知:sendEmptyMessage等,傳送資訊的方法,最終都是呼叫了SendMessageAtTime(msg,when);

而SendMessageAtTime(msg,when);方法最終的目的就是為了queue.enqueueMessage(msg, uptimeMillis);,其中msg為傳送的Message物件,uptimeMillis為SystemClock.uptimeMillis() + when

檢視系統的enqueueMessage方法,該方法最終實現在messageQueue當中,所有的message按執行的先後順序,從小到大排列

final boolean enqueueMessage(Message msg, long when) {
       
            msg.when = when; // 將執行時間設定給msg.when
            Message p = mMessages;  // 定義變數p = mMessage  ,mMessage初終指向對列的第一個Message 物件

            if (p == null || when == 0 || when < p.when) {
				// 當佇列中為空的時候,mMessage = msg
                msg.next = p;
                mMessages = msg;
                this.notify();
            } else {
				// 否則將要進入佇列的msg的執行時間和佇列中的message的執行時間進行比較,
				// 最終會使messageQueue中的所有的message按時間為順序從小到大排列
				// 即按執行的先後順序排列
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                this.notify();
            }
        }

訊息傳送成功這時候該呼叫Looper.loop方法:即完成了從MessageQueue中取出需要執行的Message,並執行我們重寫的handlMessage方法
 public static final void loop() {
	//獲得當前執行緒的looper物件及messageQueue物件
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;

	//開啟while(true)迴圈
        while (true) {
	    //從訊息佇列中取出一個message,如果message執行時間不到,那就wait等一會
            Message msg = queue.next(); // might block

	    //執行message 對應的handler中的dispatchMessage方法,即,執行我們重寫的handleMessage方法
             msg.target.dispatchMessage(msg);

            }
        }
    }