Android非同步訊息處理機制(2)源碼解析

來源:互聯網
上載者:User

Android非同步訊息處理機制(2)源碼解析

 

先後順序按照拼音排序,無關技術本身。

先簡單地總結一下Looper,MessageQueue,Message和Handler四者之間的關係:

Looper和MessageQueue
Looper對象是線程的訊息迴圈處理器,每個線程只能有一個Looper。Looper內部有一個訊息佇列MessageQueue對象,所有該線程的訊息都存放在該隊列(按照時間排序)中。android在啟動時為主線程(UI線程)自動建立一個Looper對象,而我們自己建立線程時必須要建立Looper對象(調用 Looper.prepare())。 Handler(非抽象類別)
Handler對象是Message對象的接收者和處理者。使用者通過Handler把訊息添加到訊息佇列,同時通過Handler的回調方法 hanldeMessage()處理訊息。Hanlder在構造時和一個Looper對象關聯在一起。Handler和Looper是多對一的關係,多個Handler對象可以和同一個Looper對象建立關係,反之則不行。 Message
Message是訊息的載體,是Parcelable的衍生類別。四者涉及到的主要成員變數和方法Looper類的主要成員變數和方法:
public final class Looper {    // 成員變數    static final ThreadLocal sThreadLocal = new ThreadLocal();    private static Looper sMainLooper;     final MessageQueue mQueue;    final Thread mThread;    // 成員方法    public static void prepare() {...}    private static void prepare(boolean quitAllowed) {...}    public static void prepareMainLooper() {...}    public static Looper getMainLooper() {...}    public static void loop() {...}    public static Looper myLooper() {...}    public static MessageQueue myQueue() {...}}
MessageQueue類中的主要成員方法:
public final class MessageQueue {    Message next() {...}    boolean enqueueMessage(Message msg, long when){...}}
Handler類中的主要成員變數和方法:
public class Handler {    //內部介面    public interface Callback {        public boolean handleMessage(Message msg);    }    //處理訊息相關方法    public void handleMessage(Message msg) {}    public void dispatchMessage(Message msg) {...}    private static void handleCallback(Message message) {...}    // 構造器相關方法    public Handler() {...}    public Handler(Callback callback) {...}    public Handler(Looper looper) {...}    public Handler(Looper looper, Callback callback) {...}    public Handler(boolean async) {...}    public Handler(Callback callback, boolean async) {...}    public Handler(Looper looper, Callback callback, boolean async) {...}    // 擷取Message相關方法    public final Message obtainMessage(){...}    public final Message obtainMessage(int what){...}    public final Message obtainMessage(int what, Object obj){...}    public final Message obtainMessage(int what, int arg1, int arg2){...}    public final Message obtainMessage(int what, int arg1, int arg2, Object obj){...}    // post相關方法    public final boolean post(Runnable r){...}    public final boolean postAtTime(Runnable r, long uptimeMillis){...}    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){...}    public final boolean postDelayed(Runnable r, long delayMillis){...}    public final boolean postAtFrontOfQueue(Runnable r){...}    // send相關方法    public final boolean sendMessage(Message msg){...}    public final boolean sendEmptyMessage(int what){...}    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {...}    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {...}    public final boolean sendMessageDelayed(Message msg, long delayMillis){...}    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {...}    public final boolean sendMessageAtFrontOfQueue(Message msg) {...}    // 進出訊息佇列    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {...}}
Message類中的主要成員變數和方法:
public final class Message implements Parcelable {    // 主要成員變數    public int what;    public int arg1;     public int arg2;    public Object obj;    public Messenger replyTo;    private static final Object sPoolSync = new Object();    private static Message sPool;    private static int sPoolSize = 0;    private static final int MAX_POOL_SIZE = 50;    // 主要成員方法    public Message() {} // 不建議使用    // 擷取Message相關方法    public static Message obtain() {...}    public static Message obtain(Message orig) {...}    public static Message obtain(Handler h) {...}    public static Message obtain(Handler h, Runnable callback) {...}    public static Message obtain(Handler h, int what) {...}    public static Message obtain(Handler h, int what, Object obj) {...}    public static Message obtain(Handler h, int what, int arg1, int arg2) {...}    public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {...}    public void setTarget(Handler target) {...}    public void sendToTarget() {...}    public Handler getTarget() {...}}

下面將以問答的方式解析非同步訊息處理機制。

問答解析1. ThreadLocal的作用?

解析:ThreadLocal是Thread Local Variable即執行緒區域變數的意思,通過把資料放在ThreadLocal中就可以讓每個線程建立一個該變數的副本,從而避免並發訪問的安全執行緒問題。這裡儲存Looper類的執行個體對象。

2. 為什麼在UI線程中執行個體化Handler並不需要 Looper.prepare(),而在子線程中則需要 Looper.prepare()

解析:這是因為在ActivityThread類中的main()方法調用了Looper.prepareMainLooper(),簡單代碼如下:

public static void main(String[] args) {       ...// 前面的省略了        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, ActivityThread));        }        Looper.loop();        throw new RuntimeException(Main thread loop unexpectedly exited);    }

可以看到main()方法中調用了Looper.prepareMainLooper(),而Looper.prepareMainLooper()源碼為:

  public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException(The main Looper has already been prepared.);            }            // 若已經存在Looper對象直接從ThreadLocal對象sThreadLocal中擷取            sMainLooper = myLooper();        }    }

prepare(boolean)代碼如下:

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

到這裡我們可以看到main()方法中調用了Looper.prepareMainLooper(),而Looper.prepareMainLooper()又調用了prepare(boolean)prepare(boolean)方法就會建立一個Looper對象並儲存在靜態變數sThreadLocal(ThreadLocal類執行個體)。我們來看一下Looper的構造器:

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

該構造器為私人的,且建立了一個訊息佇列執行個體,並將當前線程儲存下來。

同時在prepare(boolean)可以看到若sThreadLocal已經有一個Looper對象後再建立就會出現異常。這說明了兩點:1. 一個線程中只能有一個Looper對象;2. 一個線程中只能最多調用一次prepare()等prepare相關的方法(prepare()prepare(boolean)prepareMainLooper()(UI線程中調用))。

而我們在子線程中調用的是Looper.prepare(),來看一下這個方法的源碼:

public static void prepare() {        prepare(true);    }

可以看到同樣也是調用了prepare(boolean)

3. 執行個體化Handler系統為我們做了哪些事情?

解析:該問題主要涉及到Handler中以下幾個方法:

public Handler() {this(null, false);}public Handler(Callback callback) {this(callback, false);}public Handler(Looper looper) {this(looper, null, false);}public Handler(Looper looper, Callback callback) {this(looper, callback, false);}public Handler(boolean async) {this(null, async);}public Handler(Callback callback, boolean async) {        if (FIND_POTENTIAL_LEAKS) {            final Class 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;    }public Handler(Looper looper, Callback callback, boolean async) {        mLooper = looper;        mQueue = looper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

在上述構造器中可能傳遞的參數為:

Looper對象,用於指定Handler將訊息發送到的Looper對象。因此在執行個體化Handler對象之前需要調用 Looper.prepare()方法。 Callback介面對象,用於處理Handler發送的訊息。 boolean值,是否是非同步作業。針對Handler來說這裡都是傳遞的false。

首先,在Handler(Callback callback, boolean async)中我們看到調用了Looper.myLooper()返回Looper對象,若沒有則會拋出異常。

根據以上構造器,我們能夠有多種方式執行個體化一個handler對象,前五個構造器最終都會調用下面兩個構造器。對於Handler(Callback callback, boolean async)這個構造器沒有指定Handler的Looper對象,則使用當前線程的Looper對象。同時我們看到傳遞了Callback介面對象,將其值賦值給了mCallback,我們來看一下相關的源碼:

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

通過上述代碼我們可以看到,若傳遞Callback介面對象的話,在dispatchMessage(Message msg)中將不會執行handleMessage(msg)(當然這之前先判斷msg.callback是否為空白,這個問題之後再講),而是調用mCallback.handleMessage(msg),而該方法就是我們傳遞的Callback介面對象實現的方法。因此,我們也可以通過這種方式處理訊息。若不傳遞Callback介面對象的話,那麼就會執行handleMessage(msg),我們可以通過繼承Handler實現該方法處理訊息。

4. 執行個體化Handler對象之後,為什麼要調用 Looper.loop()?

為瞭解決這個問題我們先看一下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;        .....        for (;;) {            // 取一條訊息,沒有就會阻塞            Message msg = queue.next(); // might block            if (msg == null) {                // msg等於null,就會退出                return;            }            .....            msg.target.dispatchMessage(msg);            .....            msg.recycleUnchecked(); // 保證訊息迴圈利用。        }    }

loop()中首先擷取Looper對象me,然後根據該對象擷取對應的訊息佇列queue,下面就進入一個for(;;)死迴圈不斷地從queue取訊息,若沒有則next()(該方法比較複雜,我沒怎麼看懂,,,)方法就會阻塞。
若訊息為空白的話就會退出loop();下面有一句:msg.target.dispatchMessage(msg);,這個訊息msg的target屬性就是Handler對象,那麼這個訊息就是由該Handler來處理的。我們可以通過Message的重載方法obtainXXX()setTarget()設定Handler對象。

通過loop(),就能實現不斷處理髮送過來的訊息。

我們再來看一下Message的sendToTarget():

 public void sendToTarget() {        target.sendMessage(this);    }

可以看到通過這個方法發送訊息和通過Handler發送訊息一樣。

5. Handler中的 sendMessage()是如何將訊息發送到訊息佇列中的,而訊息佇列又是如何管理訊息的?

首先,看一下,send相關方法:

// send相關方法public final boolean sendMessage(Message msg){...} // 發送的訊息希望及時處理,但不插隊public final boolean sendEmptyMessage(int what){...} // 只發送帶有what屬性的訊息,希望訊息及時處理即可public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {...} // 希望延遲處理public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {...} // 希望訊息在指定的時間處理public final boolean sendMessageDelayed(Message msg, long delayMillis){...} // 希望延遲處理public boolean sendMessageAtTime(Message msg, long uptimeMillis) {...} // 希望訊息在指定的時間處理public final boolean sendMessageAtFrontOfQueue(Message msg) {...} // 訊息插隊,希望馬上處理

帶有Empty的方法甚至發送的不是訊息,只是what!!注意一下。

上面的send方法,除了sendMessageAtFrontOfQueue(Message),都會調用sendMessageAtTime(Message,long)。而這兩個方法中則會返回訊息佇列的enqueueMessage(Message,long)的方法,該方法就是將訊息按照時間順序插入到訊息佇列中。其實,訊息佇列是用鏈表實現的,排序方式是按照時間進行排列的。enqueueMessage(Message,long)源碼如下:

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

可以看到首先通過if判斷插入的訊息的時間是否最小,若是的話直接插入到鏈表頭部,否則進入for (;;)依次遍曆鏈表直到時間小於鏈表值,然後再插入。訊息佇列以mMessages為表頭進行儲存。

以上方法就通過Handler的send方法將訊息插入到訊息佇列中。此時還是在同一個線程中。

6. 線程之間是如何轉換的?在A線程中執行個體化Handler對象之後,調用 loop()方法迴圈檢測是否有訊息進入隊列,若有則取出並處理; 在B線程中,通過Handler對象的 sendMessage()方法發送訊息,發送過來的訊息通過一系列的處理進入到訊息佇列,這樣訊息就進入了A線程中。

通過上述兩步實現了A線程和B線程之間的通訊。
在哪個線程中,又是如何取訊息的,怎麼阻塞的?

7. 建立訊息的方式

建立訊息的方式有很多種,我們來對比一下:

Handler的 obtainMessage()系列; Message的靜態方法 obtain()系列; 使用構造器建立。

Handler中的方法實際上都是調用Message的靜態方法obtain()系列,而obtain()系列最終都會調用obtain()方法。我們來看一下它的源碼:

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

可以看到當前存在多餘的訊息時,則我們可以直接使用,而不是再建立一個新的訊息,迴圈使用訊息減少資源浪費。能夠迴圈使用訊息的原因是:每當處理完一個訊息時都會調用訊息的recycleUnchecked()該方法就會將使用過的訊息進行儲存。源碼如下:

 void recycleUnchecked() {        ... // 清空訊息內容        synchronized (sPoolSync) {            if (sPoolSize < MAX_POOL_SIZE) {                next = sPool;                sPool = this;                sPoolSize++;            }        }    }

sPool是靜態成員變數,儲存鏈表的頭,當處理過一個訊息時,(訊息池大小小於給定值50時)該訊息就會採用頭插法插入鏈表中。

8. Handler的 post方法是幹什麼用的?

我們首先回過頭來看一下dispatchMessage()方法,其中有一個判斷:

if (msg.callback != null) {    handleCallback(msg);}

只有當msg.callback為空白時才會執行下面的。訊息的callback屬性是Runnable變數,那麼該屬性是在哪裡賦值的呢?我們發現在post系列中都會調用一個方法getPostMessage():

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

可以看到該方法可以對callback屬性進行賦值,也就是說,通過post方法發送訊息的話和send一樣可以完成處理訊息(實際上在post中也是調用send方法),只不過不在handleMessage()中處理訊息,而是在handleCallback()中處理訊息。該方法的源碼會讓我們大吃一驚的:

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

直接調用了Runnable對象的run()方法,這樣一來就不是建立新線程,而是直接調用。

當然也可以在訊息的obtain()中設定callback屬性。

post方法和 send方法的區別

post方法在內部會調用send方法,並且post方法發送的是帶有處理方法的訊息,而send方法發送的是不帶有處理方法的訊息。前者訊息在自己攜帶的方法中處理,後者則只能通過handleMessage()處理

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.