Handler,looper,MessageQueue及 handlerThread 之間的關聯和用法

Handler 是什麼?這個是android面試官最喜歡問的問題,用腳趾都能回答出來,Handler可用於android多線程通信,可以用來更新UI的,用來發送message,處理message的。於是在很多地方我們都直接new 一個Handler 來使用,那麼問題來了,如果你在一個自定義線程中new一個Handler,這個Handler能用麼?能 or 不能。 Why?

先給大家上個圖,好有個整的印象:


呵呵,現在老鳥帶你揭開Handler這個神祕的面紗,大家都知道Handler 是用來發送和處理message的,那麼message存在哪裏呢?答案是MessageQueue,  Handler發送的message當然要放在MessageQueue了,那麼問題又來了,MessageQueue中的message誰在管理,是Handler?  NO, NO, 是Looper。 下面我們看看Handler的部分源碼:

 public Handler() {
        this(null, false);
 }
 public Handler(Callback callback, boolean async) {
        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;
    }

看到這裏是不是豁然開朗了,Handler需要一個Looper, Looper裏面有一個mQueue, 這個mQueue就是MessageQueue,不信?你去看Looper.java源碼。所以自定義線程中是不能就這麼簡單的new一個Handler來用滴。如果沒有這個Looper,在運行時是會拋出異常滴。那麼問題又來了,爲啥我們在Activity或Fragment裏面可以直接new一個Handler來用呢?答案是:因爲應用的主線程幫我們定義好了這個Looper,我們在Activity或Fragment裏面new一個Handler都是使用主線程的Looper。 

呵呵,說到主線程,是不是又讓人蒙啦,大名鼎鼎的android主線程,就是UI線程,老鳥在這裏給你簡單介紹下吧,更詳細的關於主線程,老鳥會單獨N篇文章講解。首先,主線程就是ActivityThread, 每一個應用有且只有一個主線程,UI改變都是在這個AcitivityThread裏面完成的。在這裏我們只關心ActivityThread是如何創建主線程Looper的,下面是ActivityThread部分源碼:

 public static void main(String[] args) {
      
        Environment.initForCurrentUser();

    
        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        Looper.loop();

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

ActivityThread的main方法裏會調用Looper.prepareMainLooper方法,這個方法就是創建主線程的Looper, 並且後面執行了Looper.loop(),這個loop()就是一個for(;;)永久循環,這個循環體裏面就是不斷判斷是否有新的message進入了Looper的MessageQueue裏面,如果有message,就把他發送給發送這個message的handler去執行。當我們在activity或fragment裏面new一個Handler的時候,其實都是拿到主線程這個Looper,然後發消息到這個Looper的MessageQueue裏。那麼我們在activity裏面是怎麼拿到這個主線程Looper的呢?在上面的Handler代碼裏我們new一個Handler的時候,會調用Looper.myLooper(),下面看看Looper的部分代碼:

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

 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
 }

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

由上面代碼可以知道prepareMainLooper調用了prepare(), prepare()裏面執行了new Looper(), 這裏新建了一個MessageQueue, 最後並且把這個looper存入了sThreadLocale裏面。所以當我們在activity或fragment裏面new Handler的時候,都會從myLooper()裏面拿到這個主線程looper啦。

那麼又有問題來了,我們自己定義的thread裏面我也想用Handler怎麼辦?這個問題現在是不是很簡單了?就是我們自己也給我們的線程生成一個Looper,不就OK了麼,對滴,就這樣。上面調用Looper的prepare()就會爲我們生成一個Looper。如何把這個looper跟我們的Handler關聯起來呢,我們可以用調用Handler(Looper looper)這樣,把他們關聯起來,最後調用Looper.loop()讓這個looper動起來,這樣你就可以用這個Handler發送處理message啦。是不是很爽。

 class MyLooperThread extends Thread{
        private final String TAG = "MyLooperThread";
        private MyHandler mHandler;
        public MyLooperThread(){       
        }
        @Override
        public void run() {
            super.run();    
            Looper.prepare();
            mHandler = new MyHandler(Looper.myLooper());
            Looper.loop();
        }

        public void sendMessage(Message msg){
            mHandler.sendMessage(msg);
        }
        class MyHandler extends Handler{
            MyHandler(Looper looper){
                super(looper);
            }
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 1:             
                        Log.i(TAG,"1111111111111");
                        break;
                    default:
                }
            }
        }
    }

上面我們就可以在handleMessage裏面處理我們自己的消息啦。

說了這麼多我們還沒說HandlerThread, 這個又是什麼鬼呢?其實這個就是android幫我們封裝了Looper的一個thread,它的實現就跟我們上面自己的MyLooperThread差不多,使用它我們就不用自己去手動調用生成Looper的代碼啦,這是不是很貼心呢。


更多精彩Android技術可以關注我們的微信公衆號,掃一掃下方的二維碼或搜索關注公共號: Android老鳥

                                                





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