Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android異步消息處理機制(2)源碼解析

Android異步消息處理機制(2)源碼解析

編輯:關於Android編程

 

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

先簡單地總結一下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()處理

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved