Android消息機(jī)制之Handler、MessageQueue、Looper源碼分析

1、什么是Android消息機(jī)制?什么是Handler?為什么要用Handler?

Android消息機(jī)制主要是指Handler的運(yùn)行機(jī)制,Handler運(yùn)行需要底層的MessageQueue和Looper支撐。其中MessageQueue采用的是單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲消息列表,Looper可以理解為消息循環(huán)。由于MessageQueue只是一個(gè)消息存儲單元,不能去處理消息,而Looper就是用來處理消息的,Looper會以無限循環(huán)的形式去MessageQueue中查找是否有新消息,如果有則取出消息并處理,否則就一直阻塞等待。Looper中還有一個(gè)特殊的概念是ThreadLocal,ThreadLocal的作用是可以在每個(gè)線程中存儲數(shù)據(jù),在Handler創(chuàng)建的時(shí)候會采用當(dāng)前線程的Looper來構(gòu)造消息循環(huán)系統(tǒng),Handler內(nèi)部就是通過ThreadLocal來獲取每個(gè)線程的Looper,線程默認(rèn)是沒有Looper的,在使用Handler之前必須先為主線程創(chuàng)建Looper。大家可能有疑惑了,我們在代碼中使用Handler的時(shí)候,并沒有為主線程去創(chuàng)建Looper的代碼,為什么也可以正常使用呢?那是因?yàn)橹骶€程中ActivityThread被創(chuàng)建時(shí)已經(jīng)初始化主線程的Looper。

Handler主要用于異步消息的處理:當(dāng)發(fā)出一個(gè)消息(Message)之后,首先進(jìn)入一個(gè)消息隊(duì)列(MessageQueue),發(fā)送消息的函數(shù)(sendMessage())即刻返回,而另外一個(gè)部分在消息隊(duì)列中逐一將消息取出,然后對消息進(jìn)行處理,也就是發(fā)送消息和接收消息不是同步的處理。 這種機(jī)制通常用來處理相對耗時(shí)比較長的操作,比如從服務(wù)端拉取一些數(shù)據(jù)、下載圖片等。

在Android中規(guī)定訪問UI只能在主線程中進(jìn)行(因?yàn)锳ndroid的UI控件不是線程安全的,如果在多線程中并發(fā)訪問可能會導(dǎo)致UI控件處于不可預(yù)期的狀態(tài)),如果在子線程訪問UI,程序會拋出異常。如在子線程中拉取服務(wù)端數(shù)據(jù)之后需要更新UI上控件展示內(nèi)容,這個(gè)時(shí)候就需要切換到主線程去操作UI,此時(shí)就可以通過Handler將操作UI的工作切換到主線程去完成。因此,系統(tǒng)之所以提供Handler,主要原因就是為了解決在子線程中無法訪問UI的矛盾。

2、Handler簡單使用

2.1、創(chuàng)建Handler實(shí)例的兩種方式

2.1.1 通過Handler.Callback

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });

2.1.2 通過派生Handler的子類

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

2.2、Handler發(fā)送消息常用的兩種方式

2.2.1 sendMessage

Message message = new Message();
message.obj = "JokerWan";
message.what = 666;
// 發(fā)送消息
handler.sendMessage(message);
// 發(fā)送延遲消息
handler.sendMessageDelayed(message,3000);

2.2.2 post

handler.post(new Runnable() {
    @Override
    public void run() {
        // action           
    }
});     

3、Handler運(yùn)行機(jī)制原理圖

Android消息機(jī)制原理圖.png

4、Handler運(yùn)行機(jī)制源碼分析

溫馨提示:為了方便大家閱讀,我這里貼出的代碼會對源碼進(jìn)行刪減,只保留部分跟本文相關(guān)的關(guān)鍵代碼,其他代碼用...代替

4.1、主線程Looper的創(chuàng)建

首先我們看到ActivityThread類中的main()方法的代碼

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

        ...

        Looper.prepareMainLooper();

        ...

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

        ...
        
        Looper.loop();

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

其中有兩個(gè)關(guān)鍵代碼Looper.prepareMainLooper();Looper.loop();,我們先看Looper.prepareMainLooper();里面做了哪些事情

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

繼續(xù)跟進(jìn)其中調(diào)用的prepare(false);方法

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

這個(gè)方法中用到了sThreadLocal變量,找到它的聲明

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

類型是ThreadLocal,泛型是Looper,ThreadLocal是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲類,通過它可以在指定的線程存儲數(shù)據(jù),其他線程無法獲取該線程數(shù)據(jù)。我們繼續(xù)跟進(jìn)下sThreadLocal.get()方法,ThreadLocal#get()

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

調(diào)用getMap方法并傳入當(dāng)前線程t,返回保存當(dāng)前線程中的數(shù)據(jù)ThreadLocal.ThreadLocalMap的對象mapThreadLocalMap類似于HashMap,只不過ThreadLocalMap僅僅是用來存儲線程的ThreadLocal數(shù)據(jù),首先通過getMap()獲取當(dāng)前線程的ThreadLocal數(shù)據(jù)的map,接著用ThreadLocal的對象作為key,取出當(dāng)前線程的ThreadLocal數(shù)據(jù)的map中存儲的result,由于前面sThreadLocal變量聲明的時(shí)候約定的泛型是Looper,所以這里返回result對象就是Looper對象,繼續(xù)回到prepare()方法,首先取出當(dāng)前線程的Looper對象,校驗(yàn)下Looper對象的唯一性,然后new Looper(quitAllowed)并保存在ThreadLocal當(dāng)前線程中的數(shù)據(jù)中。

接著跟進(jìn)Looper的構(gòu)造方法

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

在Looper的構(gòu)造函數(shù)中創(chuàng)建了一個(gè)MessageQueue的對象,并保存在Looper的一個(gè)全局變量mQueue中,所以,每個(gè)Looper對象都綁定了一個(gè)MessageQueue對象。

4.2、Handler發(fā)送消息到MessageQueue

4.2.1 Handler構(gòu)造器

    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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

首先調(diào)用Looper.myLooper();方法獲取Looper對象,并保存在mLooper變量中,看一下Looper#myLooper()

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

若獲取的Looper對象為null,則拋出異常Can't create handler inside thread xxThread that has not called Looper.prepare(),因?yàn)樯厦嫖覀円呀?jīng)分析過了,Looper對象的創(chuàng)建是在prepare()方法中。
從ThreadLocal中獲取當(dāng)前線程(主線程)的Looper對象,接著將Looper中的MessageQueue保存到mQueue中,并將callback賦值給mCallback,當(dāng)我們通過匿名內(nèi)部類創(chuàng)建Handler時(shí)callback為null。

4.2.2 Handler#sendMessage

    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;
        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);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看到不管是調(diào)用sendMessage還是sendMessageDelayed,還有后面會講到的post(runnable),最終都會調(diào)用enqueueMessage方法,方法里面首先將Handler對象賦值給msg.target,后面會通過msg.target獲取Handler對象去處理消息(后面會講到),然后調(diào)用了mQueueenqueueMessage(msg, uptimeMillis)方法,
跟進(jìn)MessageQueue#enqueueMessage()

    boolean enqueueMessage(Message msg, long when) {
        ...
        synchronized (this) {
            ...
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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;
            }
            ...
        }
        return true;
    }

MessageQueue是一個(gè)消息隊(duì)列,主要包含兩個(gè)操作,插入和讀取,插入就是上面這個(gè)方法enqueueMessage,讀取是next方法,后面會講到。盡管MessageQueue叫消息隊(duì)列,但它內(nèi)部實(shí)現(xiàn)并不是隊(duì)列,而是用單鏈表的數(shù)據(jù)結(jié)構(gòu)來維護(hù)消息列表,主要原因是單鏈表在插入和刪除上效率比較高。從enqueueMessage方法的實(shí)現(xiàn)來看,它的主要操作就是單鏈表的插入操作,將傳進(jìn)來的msg插入到鏈表中,并將msg.next指向上一個(gè)傳進(jìn)來的Message。

4.2.3 Handler#post

查看post方法源碼

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

發(fā)現(xiàn)原來post方法也是調(diào)用的sendMessageDelayed方法去send Message,只是把我們傳進(jìn)來的Runnable對象通過調(diào)用getPostMessage方法構(gòu)造成一個(gè)Message

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

通過Message.obtain()獲取一個(gè)Message,將Runnable對象賦值給m.callback,并返回Message,到這里我們就知道了,原來post(Runnable r)方法只是把r放進(jìn)一個(gè)message中,并將message發(fā)送出去

4.3、Looper輪詢MessageQueue并將消息發(fā)送給Handler處理

前面我們已經(jīng)分析過,在ActivityThread類中的main()方法中會調(diào)用Looper.prepareMainLooper();Looper.loop();,Looper.loop();就是開啟輪詢的操作

    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;

        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        ...

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ...

            try {
                msg.target.dispatchMessage(msg);
                ...
            } finally {
                ...
            }
            ...
            msg.recycleUnchecked();
        }
    }

首先獲取到Looper對象和Looper對象里面的MessageQueue,然后構(gòu)造一個(gè)死循環(huán),在循環(huán)里面通過調(diào)用MessageQueue的next()方法取出Message分發(fā)給Handler去處理消息,直到取出來的msg為null,則跳出循環(huán)。msg.targetenqueueMessage方法中賦值為發(fā)送此Message的Handler對象,這里取出Handler對象并調(diào)用其dispatchMessage(msg)去處理消息,而Handler的dispatchMessage(msg)是在創(chuàng)建Handler時(shí)所使用的Looper中執(zhí)行的,這樣就成功的將代碼邏輯切換到了指定的線程去執(zhí)行。來看一下MessageQueue的next()具體實(shí)現(xiàn)

Message next() {
        ...
        for (;;) {
            ...
            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 = -1;
                }

                ...
        }
    }

可以看到next是一個(gè)阻塞操作,開啟一個(gè)死循環(huán)來取Message,當(dāng)沒有Message時(shí),next方法就會一直阻塞在那里,這也導(dǎo)致Looper.loop()方法也一直阻塞。

4.4、Handler處理消息

Looper對象從MessageQueue中取到Message然后調(diào)用Handler的dispatchMessage(msg)去處理消息

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

首先檢查msg的callback是否為null,不為null則調(diào)用handleCallback(msg)來處理消息,還記得上面我們分析的post(runnable)嗎,這里從msg取出的callback就是調(diào)用Handler的post方法傳入的Runnable對象,看下handleCallback(msg)代碼實(shí)現(xiàn)

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

實(shí)現(xiàn)很簡單,就是取出Runnable對象并調(diào)用其中的run方法。

dispatchMessage方法接著檢查mCallback是否為null,不為null就調(diào)用其handleMessage(msg)方法,mCallback類型為Callback接口,上面我們講過Handler創(chuàng)建的兩種方式,第一種方式就是在Handler的構(gòu)造器中通過匿名內(nèi)部類傳入一個(gè)實(shí)現(xiàn)了Handler.Callback接口的對象,并在構(gòu)造器賦值給mCallback。

dispatchMessage方法最后調(diào)用Handler的handleMessage(msg)方法來處理消息,就是上面我們講到的Handler創(chuàng)建的第二種方式:派生一個(gè)Handler的子類并重寫其handleMessage方法來處理消息。

5、主線程的消息循環(huán)

前面我們分析了ActivityThread類中的main()會創(chuàng)建Looper和MessageQueue,并將MessageQueue關(guān)聯(lián)到Looper中,調(diào)用Looper的loop方法來開啟主線程的消息循環(huán),那么主線程的消息是怎么發(fā)送的呢?ActivityThread類內(nèi)部定義了一個(gè)Handler的子類H,ActivityThread就是通過這個(gè)內(nèi)部類H來和消息隊(duì)列進(jìn)行交互并處理消息。

ActivityThread通過 ApplicationThread 和 AMS 進(jìn)行進(jìn)程間通信, AMS以進(jìn)程間通信的方式完成 ActivityThread 的請求后會回調(diào) ApplicationThread 中的 Binder 方法,然后 ApplicationThread會向 H 發(fā)送消息,H 收到消息后會將 ApplicationThread中的邏輯切換到 ActivityThread 中去執(zhí)行,即切換到主線程中取執(zhí)行,這個(gè)過程就是主線程的消息循環(huán)模型。

6、總結(jié)

  1. Handler 的背后有 Looper、MessageQueue 支撐,Looper 負(fù)責(zé)消息分發(fā), MessageQueue 負(fù)責(zé)消息管理;
  2. 在創(chuàng)建 Handler 之前一定需要先創(chuàng)建 Looper;
  3. Looper 有退出的功能,但是主線程的 Looper 不允許退出;
  4. 異步線程的 Looper 需要自己調(diào)用 Looper.myLooper().quit(); 退出;
  5. Runnable 被封裝進(jìn)了 Message,可以說是一個(gè)特殊的 Message;
  6. Handler.handleMessage() 所在的線程是 Looper.loop() 方法被調(diào)用的線程,也可
    以說成 Looper 所在的線程,并不是創(chuàng)建 Handler 的線程;
  7. 使用內(nèi)部類的方式使用 Handler 可能會導(dǎo)致內(nèi)存泄露,即便在 Activity.onDestroy
    里移除延時(shí)消息,必須要寫成靜態(tài)內(nèi)部類;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容