江南帶你從源碼看handler內部實現的機制

介紹handler之前首先我們來看下ThreadLocal類的用法

private static ThreadLocal<String> sthread;
    public static void main(String[] args) {
      sthread = new ThreadLocal<>();
      sthread.set("這是我的第一個項目");
      show();
      new Thread(new Runnable() {

        @Override
        public void run() {
            show();

        }
    });
    }

    public static void show(){
        System.out.println(sthread.get());
    }

ThreadLocal就是相當於是一個map集合,他的鍵就是當前的線程,值就是自己設置的值,如果ThreadLocal在主線程中set了值,在子線程中是沒法獲得的,這就是ThreadLocal的一個重要特點,可以將數據和當前線程綁定。
當我們應用開啓的時候系統會調用他的ActivityThread類的Loop.PrepareXXX方法,然後這個方法會調用Loop.prepare()方法。
在這個方法裏面我們把ThreadLocal類的對象所謂Loop的一個成員變量記錄下來,這樣有個好處在於下文handler。或者調用loop.loop()的時候很快的通過ThreadLocal獲取到當前的looper對象。進而獲得MessageQueen。

public final class Looper {
    private static final String TAG = "Looper";

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

    final MessageQueue mQueue;
    final Thread mThread;
    volatile boolean mRun;

    private Printer mLogging;

這裏寫圖片描述

這裏寫圖片描述

上面的方法同時是將looper對象綁定到當前線程上面(爲下面的handler的時候回調用sThreadlocal.get()獲得looper)

這裏寫圖片描述

同時這個方法還把當前looper類的對象與相前線程綁定一起

其實內部就是系統在創建looper對象的prepare()方法的時候同時創建消息隊列MessageQueen,然後把looper對象與當前線程綁定在一起

查看Handler源碼可知
創建Handler對象的時候我們

 public Handler() {
        this(null, false);
    }

然後調用到了

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();
    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對象,調用了prepare()方法創建了消息隊列,然後當handler對象一創建的時候調用構造函數,通過ThreadLocal的get()方法獲得當前線程在ThreadLocal中設置的looper引用,然後通過looper對象的引用獲得MessageQueen消息隊列。當Handler對象獲得了消息隊列的引用的時候,

hanlder.sendMessage(msg)底層源碼可知
  */
    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;//因爲當前的是handler對象獲得了looper引用,進而獲得了MessageQueen對象的引用,自然能執行這個方法
        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);
    }

再然後
這裏寫圖片描述
注意這裏面還有一個注意點就是msg.target=this,也就是將當前的Handler對象引用賦給msg的target屬性、

下面我們看看looper.loop()方法執行了什麼?
這裏寫圖片描述
接着追了myLooper()
這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述
for (;;) {//死循環,會不會把主線程卡死???
不會被卡死。
涉及到Linux下的通訊機制,管道通訊的概念,管道就是內存中的一個特殊的一個文件,這個文件有兩個句柄(其實就是java中的引用),一個是讀的引用,一個是寫的引用,當寫的一端寫入內容時,讀的一端就會被喚醒,讀的一端在沒有讀到消息時會休眠

總結looper.loop()做了什麼?
其實就是首先通過myLooper()方法獲得looper對象引用,然後通過looper對象的引用獲得消息隊列的引用,然後通過一個死循環不斷地從消息隊列中獲取到消息,通過msg.this獲得handler,然後調用handler對象的disPatchmessage()方法執行handMessage()【由我們外部重寫】,這也就消息就循環執行起來了。。。

這裏寫圖片描述

這裏寫圖片描述

其實作爲一個對技術熱愛的程序員我們不可能僅僅停留在這裏,試問假如主線程給子線程發消息那我們該如何去做?
由於安卓的主線程比較特殊在應用被開啓的時候就會調用Looper.prepare()方法創建了Looper.MessageQueen,那我們我們子線程如果也想實現主線程給其發消息,那也得手動創建Looper對象,進而獲取到MessageQueen隊列,然後不斷地往裏面發消息。然後通過一個Looper.loop()方法不斷地從裏面取消息,但是這裏有個弊端就是由於Looper.loop()會堵塞住子線程導致子線程沒法結束,所以一般這種方式僅僅用於測試,很少這麼去用。

public class MainActivity extends Activity {
    private Handler subHandler;

    //1
    private Handler mainHandler = new Handler(){
        //2
        @Override
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 1:
                Toast.makeText(MainActivity.this, msg.toString(), Toast.LENGTH_SHORT).show();
                break;

            default:
                break;
            }
        };
    };


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

        //創建一個子線程
        Thread subThread = new Thread(new Runnable() {

            @Override
            public void run() {
                //(1)
                Looper.prepare();
                //(2)
                subHandler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        Toast.makeText(MainActivity.this, msg.toString(), Toast.LENGTH_LONG).show();
                    }
                };
                Log.d("tag", "我是loop前面的代碼");
                //(3)
                Looper.loop();//死循環,這樣子線程就一直存在
                Log.d("tag", "我是loop後面的代碼");

            }
        });
        subThread.start();


    }

    //開啓子線程,模擬耗時任務
    public void start(View view){

        new Thread(new Runnable() {

            @Override
            public void run() {
                //模擬耗時操作
                SystemClock.sleep(5000);

                Message msg = new Message();
                msg.obj = Thread.currentThread().getName();
                msg.what = 1;
                //3
                mainHandler.sendMessage(msg);

            }
        }).start();


    }
    //主線程給子線程發送消息
    public void send2SubThread(View view){
        //發送消息
        subHandler.obtainMessage(2, "我是主線程給你發送的數據").sendToTarget();
    }

}
發佈了35 篇原創文章 · 獲贊 14 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章