Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android消息機制Handler、Looper、MessageQueue源碼分析

Android消息機制Handler、Looper、MessageQueue源碼分析

編輯:關於Android編程

1. Handler Looper MessageQueue的關系

這裡寫圖片描述

2.源碼分析

下圖表示了Handler、Looper、MessageQueue、Message這四個類之間的關系。
這裡寫圖片描述

Handler必須與一個LoZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcGVyudjBqqOsz+C52Exvb3Blcr72tqjBy7jDSGFuZGxlcrvhz/LExLj2TWVzc2FnZVF1ZXVlt6LLzU1lc3NhZ2UKw7/Su7j2TG9vcGVy1tC2vLD8uqzSu7j2TWVzc2FnZVF1ZXVlCkhhbmRsZXLW0LXEbVF1ZXVl0v3Tw7XEvs3Kx9Pr1q652MGqtcRMb29wZXK1xE1lc3NhZ2VRdWV1ZQqyu7ncSGFuZGxlctTaxMS49s/fs8y3osvNTWVzc2FnZaOsxuTW0LXEbUNhbGxiYWNru/JoYW5kbGVNZXNzYWdlt723qLa8u+HU2kxvb3Blcsv51Nq1xM/fs8zWtNDQIDxicj4K0tTJz7y4zPW94cLbvau74dTaz8LD5rXE1LTC67fWzvbW0L3iys2howoKCgoKPGgyIGlkPQ=="21-handler的創建">2.1 Handler的創建

實際使用中通常在Activity中new Handler()創建一個Handler。之前說過,Handler必須與一個Looper關聯,這個構造方法怎麼沒有給成員變量mLooper賦值?分析以下源碼,可以看到通過調用Looper.myLooper()就可以獲取當前線程的Looper。若獲取不成功,則會拋出RuntimeException,創建Handler失敗。獲取成功後,就會給Handler的成員變量mLooper和mQueue賦值。
關於Looper.myLooper()的源碼分析請參見Looper源碼分析。

public Handler() {
    this(null, false);
}

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        // 這一塊代碼通過反射機制判斷該繼承與Handler的類是不是匿名內部類或成員類或局部類且不為static
        // 若以上條件都滿足,則會提示要將該Handler設為static,否則會內存洩露
        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());
        }
    }
    // 獲取當前線程關聯的Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        // 若該線程沒有Looper,則Handler無法創建
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    // Handler中的mQueue引用的就是Looper中的mQueue
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

上面兩個Handler構造方法有個不足,在哪個線程調用該構造方法,該Handler就必須與當前線程的Looper進行關聯,所以Handler還提供了另外一個構造方法,該構造方法不管在哪個線程調用,可以與特定的Looper進行關聯,源碼如下:

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

2.2 Handler向MessageQueue發送Message

Handle有兩組方法向MessageQueue發送Message。

sendMessage postRunnable

分析以下源碼可知,postRunnable先是創建一個Message,並將參數Runnable賦給Message的callback成員變量。兩組方法最終調用的都是sendMessageAtTime。

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

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

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

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    // 這裡將delay時間轉換為了以boot時間為基准的時間,這樣所有的Message有了同樣的時間基准,從而可以排序
    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);
}

sendMessageAtTime調用了方法enqueueMessage。方法先將該Handler的引用賦給msg的成員變量target,該target會在消息派發的時候起作用,它標識了該消息應該交給哪個Handler處理。最後調用MessageQueue.enqueueMessage方法。

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

2.3 Message的屬性

在了解MessageQueue如何處理Message之前,先了解一下Message有哪些重要屬性。

屬性 意義 int what 用於指明消息類型 int arg1 int arg2 Object obj 用於攜帶參數 Messenger replyTo 用於跨進程通信 Handler target 在消息派發時用於指明哪個handler處理消息 Runnable callback 處理消息時執行的回調方法 long when 用於指明何時派發消息 Message next 指向下一個Message節點,用於構建消息鏈表

2.4 將Message插入MessageQueue

MessageQueue.enqueueMessage源碼如下:

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || 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的target是否為空,是否正在被使用,MessageQueue是否處於quiting狀態,若是則會拋出異常。
以下代碼片段為關鍵邏輯。

if (p == null || when == 0 || when < p.when) {
    // 將消息插入鏈表頭。
    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;
}

MessageQueue為一個消息鏈表,節點按消息派發時刻先後的順序排列,p為Message鏈表頭。
當MessageQueue為空時(p == null),或消息派發時刻為0(when == 0),或消息派發時刻早於消息鏈表頭消息的派發時刻時,就將消息插入消息鏈表頭。
反之,則遍歷鏈表,尋找插入位置,將該消息插入鏈表。

2.4 Looper消息循環並派發消息

消息循環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;

    ...

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

        ...

        msg.target.dispatchMessage(msg);

        ...

        msg.recycleUnchecked();
    }
}

queue.next()取出消息。
msg.target.dispatchMessage(msg)相當於handler.dispatchMessage(msg),派發消息,調用目標handler回調方法dispatchMessage。

2.5 處理消息

消息處理回調方法源碼如下:

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

判斷Message的callback是否不為空,當handler使用postRunnable時,參數Runnable就付給了msg.callback,這種情況就不為空,則會執行該callback。
判斷mCallback時候為null,mCallback是Handler的成員變量,是在構造方法裡賦值的,若不為空則執行mCallback.handleMessage(msg)。
當msg.callback和mCallback都為null時,執行handleMessage(msg)。

上述過程正是Android消息機制。

5. Active Object模式

通常在這樣的情況下使用Handler,由於在非UI線程不能執行更新UI操作,所以在非UI線程使用Handler發送更新UI消息,Handler在UI處理消息並執行更新UI操作。這個過程其實是要將方法的調用與方法的執行解耦。
發送更新UI消息相當於方法的調用
執行更新UI操作相當於方法的執行
這正是Active Object模式要解決的問題。

Active Object的概念在《Java編程思想》第4版中有所提及

第21章 並發
21.10 活動對象

之所以稱這些對象是“活動的”,是因為每個對象都維護著它自己的工作器線程和消息隊列,並且所有對這種對象的請求都將進入隊列排隊,任何時刻都只能運行其中的一個。

一般情況下,一個對象調用其某個方法,該方法將會在當前線程中執行,即在哪個線程調用就在哪個線程執行,對象完全被動,無法控制方法的執行線程,所以一般的對象稱為被動對象Passive Object。
而主動對象就不同了,它維護著自己的工作線程以及消息隊列,不管在哪個線程調用主動對象的方法,調用請求都會進入隊列排隊,它自己的工作線程從消息隊列中取出調用請求並執行,方法的調用與方法的執行解耦,並且方法的執行線程完全受自己控制,所以稱為主動對象。
以Handler為例,Handler維護了一個Looper和一個消息隊列MessageQueue,Looper決定了Handler在哪個線程處理消息,不管在哪個線程調用Handler.sendMessage(msg),消息處理程序都會在Looper所在線程中執行。

這裡寫圖片描述
在Handler這個例子中,組成結構對應關系如下:
Proxy -> Handler.sendMessage()
Scheduler -> Looper
ActivationList -> MessageQueue
MethodRequest -> Message
ConcreteMethodRequest -> 不同的消息類型
Servant -> Handler的消息處理方法handleMessage

參考資料

Java編程思想第四版 面向模式的軟件架構·卷2:並發和聯網對象模式
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved