天天看點

Handler消息機制 源碼解讀

基本概念

Handler消息機制的作用

大家知道子線程沒有辦法對UI界面上的内容進行操作,如果操作,将抛出異常:CalledFromWrongThreadException,為了讓子線程能間接操作UI界面,Android中引入了Handler消息傳遞機制,通過Handler切換到主線程進行UI操作。

Handler、Looper、MessageQueue、Message的關系是什麼?

  • Handler用于發送和處理消息。而發出的Message經過一系列的周轉後,最終會傳遞回Handler中,最後更新UI。
  • Message是線上程之間傳遞的消息,它可以在内部攜帶少量的資訊,用于在不同線程之前交換資料。
  • MessageQueue是消息隊列,用于存放Message。Message在消息隊列中,等待Looper取出。每個線程中隻會有一個MessageQueue對象。
  • Looper是每個線程中的MessageQueue的管家,調用Looper的loop()方法後,就會進入一個無限循環中,每當MessageQueue中存在一個Message,Looper對象就會将其取出,傳遞到Handler中進行處理。每個線程中隻會有一個Looper對象。

Handler消息機制疑問

從上面的基本概念中,我們不難會産生疑問,比如:

  • 在Message的周轉過程中,是怎麼切換到主線程的?
  • 如果目前線程中new了多個Handler,它們發送消息後會錯亂嗎?會不會找錯Handler對象。
  • Handler的post方法和sendMessage有什麼差別?
  • 怎麼保證每個線程中隻能存在一個Looper和MessageQueue?
  • 子線程為什麼不能直接new Handler?
  • 主線程為什麼不用手動建立Looper?
  • 主線程中的Looper無限循環,為什麼沒有造成ANR?

下面,我将從源碼角度,對以上疑問進行探索。看完本篇部落格,相信你心裡就會有答案了。

初識API

Message

我們先來簡單認識一下Message

public int what;
    public int arg1; 
    public int arg2;
    public Object obj;
           

上面幾個的用法,大家都知道,就不用介紹了。我們來看看下面的。

Handler target;
    Runnable callback;
           

Message中可以儲存Handler對象,也可以儲存Runnable,具體用法看完本篇部落格就知道了。

還記得

Message.obtain

用法嗎?

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = ; 
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
           

因為Message被處理完過後,是不會直接回收的,預設會儲存一定數量的Message以供複用。我們可以使用

Message.obtain

複用,以免建立多餘的Message。

MessageQueue

MessageQueue是什麼?我們可以給它了解成一個集合,比如List,我們可以添加消息,也可以讀取消息,移除消息。當然MessageQueue的内部是通過一個單連結清單的資料結構來實作的,了解起來可能有點費勁,我們隻需知道有兩個重要的方法

enqueueMessage

插入消息,

next

取出消息。

//======================插入消息=================================
    boolean enqueueMessage(Message msg, long when) {
            //...
            //省略了部分代碼
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when ==  || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }


    //======================取出消息=================================

Message next() {
             //...
            //省略了部分代碼
        int pendingIdleHandlerCount = -; // -1 only during first iteration
        int nextPollTimeoutMillis = ;
        for (;;) {
            if (nextPollTimeoutMillis != ) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -;
                }
              //...
            //省略了部分代碼
        }
    }
           

工作原理

發送消息(将Message送出到MessageQueue)

Handler平時用的都比較多,一般都會直接使用

mHandler.sendMessage

進行發送消息,然後重寫Handler的

handleMessage

進行接收和處理消息。那麼

mHandler.sendMessage

到底做了什麼事呢?

public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, );
    }

public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < ) {
            delayMillis = ;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
           

我們可以發現,在Handler内部,其實調用的是

sendMessageDelayed

,然後

sendMessageDelayed

中又調用了

sendMessageAtTime

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

sendMessageAtTime

中我們看到了什麼?沒錯

MessageQueue

,而且

MessageQueue

不可為空,否則會抛出異常,你可能為疑問,這個

MessageQueue

是從哪裡來的,不要急,下面馬上就會介紹,在Handler的構造方法那裡就能看到。我們暫且不管

MessageQueue

是怎麼工作的,我們隻需知道,我們目前的任務是将

Message

塞進

MessageQueue

中,我們接下來看看

enqueueMessage

中做了什麼。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
           

我們可以發現,這裡調用了MessageQueue中的

queue.enqueueMessage

方法将Message插入到隊列中去,到此為止,我們完成了第一步。

我們還能發現這一行

msg.target = this;

,沒錯,将目前的Handler對象綁定給了Message,這也就能解釋,為什麼Message一番周轉之後,仍然知道該傳遞給哪個Handler對象。

取出消息(将Message從MessageQueue中取出)

Message

塞給了

MessageQueue

後,現在就該輪到

Looper

登場了。Looper是怎麼從

MessageQueue

取出中取出

Message

的呢。

Looper的屬性

先來簡單看一下Looper的三個屬性。

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper; 
    final MessageQueue mQueue;
           

可以看出

Looper

中會綁定一個對應的

MessageQueue

,還有一個線程變量

sThreadLocal

子線程中為什麼不能直接建立Handler

在介紹之前,我們先來解釋下,為什麼不能直接在子線程中new Handler。

大家都知道線上程中使用Handler如下,但是可能不知道為什麼要這麼寫。

Looper.prepare();
  Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
  Looper.loop();
           

那麼,我們先來看一下Handler構造方法,Handler構造方法有兩種,一種是顯示指定Looper對象的,另一種是不顯示指定Looper的(會預設擷取目前線程的Looper),所有不顯示指定Looper的構造方法都會在内部轉為調用以下構造。

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

我們可以發現隻要mLooper為空,就會抛出異常。不為空的話就連同

MessageQueue

指派給目前Handler,你可能又想問,Looper的

MessageQueue

是怎麼來的,莫急,待會會介紹Looper的構造方法。我們先看看

Looper.myLooper()

方法

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

sThreadLocal

是什麼鬼?

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
           

看到這裡終于明白了,原來用了

ThreadLocal

啊,

ThreadLocal

是Java用來存儲線程變量的api。也就是說,假如我們在主線程使用

sThreadLocal

存儲了一個Looper,在子線程也使用了

sThreadLocal

存儲了一個Looper,他們互不幹擾。每個線程取出來的值都是不一樣的。這也就保證了,每一個線程都有一個單獨的Looper。

那麼如何在子線程使用Handler呢?相信大家都有思路了,隻要保證

sThreadLocal.get()

能取到值就行,我們可以在new 之前給目前線程一個Looper對象。api中已經提供了方法,

Looper.prepare()

如下。

public static void prepare() {
        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對象,否則還是會抛出異常。為什麼要強制隻能有一個Looper對象呢?當然是因為Android的單線程模型啊。如果允許多個Looper那麼和在子線程中直接處理沒有任何差別,會導緻一系列的并發問題。

sThreadLocal.set(new Looper(quitAllowed))

給目前線程綁定一個Looper,我們來看一下Looper的構造。

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

從源碼可以看出,與此同時,也給Looper綁定了一個MessageQueue對象。

Looper取消息

那麼,Looper到底是怎麼從MessageQueue中取出Message的呢。我們來看下

Looper.loop()

public static void loop() {
        final Looper me = myLooper();//擷取目前線程的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue; //擷取Looper對應的消息隊列
         //...
         //省略了部分代碼
        for (;;) {//死循環
            Message msg = queue.next();  //從消息隊列中取出一下個Message
              //...
             //省略了部分代碼
            msg.target.dispatchMessage(msg);//msg.target就是Handler,調用Handler的dispatchMessage方法處理消息

              //...
             //省略了部分代碼
            msg.recycleUnchecked();//回收Message
        }
    }
           

流程非常清晰,通過一個死循環,不停調用

MessageQueue

next()

方法,取出Message,然後看到了沒

msg.target

,前面我們發送消息時,綁定的Handler對象。經曆了一番周轉變換了線程,又交給了Handler對象的

dispatchMessage

中進行處理消息。

處理消息(Handler調用

dispatchMessage

處理消息)

在介紹處理消息之前,我們先來認識一下Handler的其他使用方法。我們知道隻要重寫Handler的

handleMessage

方法,然後就可以接收消息了。但是,我如果不想重寫

handleMessage

呢?有沒有其他方法?當然是有的,請往下看。

Handler的構造參數

前面我也提到了,Handler的構造方法分為兩種,顯示指定Looper與否,其實這兩種都是一樣的,沒有本質上的差別。但是我們忽略了Handler構造方法中的其他參數,現在再來看一遍。

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

有沒有發現一個Callback參數,其實它是一個接口,也是用于處理消息的。

public interface Callback {
        public boolean handleMessage(Message msg);
    }
           

使用mHandler.post發送消息。

mHandler.sendMessage

這種方法大家都很常用,也很簡單。大家都知道Handler有個post方法,那麼它和sendMessage有什麼差別呢?我們來看一下源碼。

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), );
    }
           

從上面可以看出,使用post,仍然會包裝為Message,然後調用

sendMessageDelayed

進行發送消息。先來看一下到底是怎麼包裝的,如下

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
           

包裝給了一個callback,就是前面提到的Runable。

Handler處理消息

在介紹完Handler的其他用法後,現在回到Handler的

dispatchMessage

中。

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
           

看到沒有,

if (msg.callback != null)

,也就是說,如果是使用post發送的,會調用

handleCallback

進行處理。

private static void handleCallback(Message message) {
        message.callback.run();
    }
           

直接走了Runable的run方法。不會走Handler的

handleMessage

然後

if (mCallback != null)

判斷那個接口有沒有實作,如果實作了,直接走接口。最後才是調用Handler内部的

handleMessage

方法。

最後

  • 主線程為什麼不用手動建立Looper?

我們來看一個類

ActivityThread

,這個類是Android的入口,我們可以找到一個久違的方法

public static void main(String[] args)

public static void main(String[] args) {
       //...
       //省略了部分代碼

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        //...
       //省略了部分代碼
        Looper.loop();

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

從上面可以看出,Android在啟動一個應用時,已經預設給主線程開啟了Looper。

  • 主線程中的Looper無限循環,為什麼沒有造成ANR?

什麼情況下會造成ANR呢?隻有當主線程的Handler處理一個消息耗時過長才會ANR。

Looper無限循環,不是一個簡單的死循環,當消息隊列中沒有消息時,就會阻塞休眠,釋放cpu資源。

我們Activity之是以有生命周期,也依賴于Handler,是因為

ApplicationThread

根據系統

ActivityManageService

發來的事件,然後發送消息來排程生命周期的。以下是

ActivityThread

中處理消息相關的部分代碼。

public void handleMessage(Message msg) {
              //..
            //省略了部分代碼
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;

                case PAUSE_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&) != , msg.arg2,
                            (msg.arg1&) != );
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
             //..
             //省略了部分代碼
            }

        }
           

END