Android開發Handler原始碼分析

王世暉發表於2016-05-19

為什麼使用Handler

Android中UI控制元件的訪問是執行緒不安全的,加鎖同步訪問會影響效能,因此設定只能一個執行緒更新UI,就是主執行緒,或者說是UI執行緒。在UI執行緒中不能進行耗時的操作,耗時操作需要開啟一個新的工作執行緒,工作執行緒不能更新UI,因此工作執行緒通過Handler通知UI執行緒更新UI

Handler構造器分析

建立匿名Handler或者內部Handler的時候,IDE會提示記憶體洩露的風險,因為內部類持有外部類引用,修改為靜態內部類即可

if (FIND_POTENTIAL_LEAKS) {
    final Class<? extends Handler> 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());
    }
}

Handler內部幾個重要的屬性

        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;

通過建構函式對屬性進行賦值。類屬性的命名規則以小寫字母m開頭,命名的規範值得學習。
其他幾個過載的建構函式功能類似,對類屬性的賦值。

通過Handler傳送訊息

Handler有多個傳送訊息的方法,但最終都呼叫了

public boolean sendMessageAtTime(Message msg, long uptimeMillis)

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

傳送訊息就是把訊息插入到訊息佇列,該訊息在訊息佇列的位置由該訊息處理的時間when決定

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

在這裡將msg的target屬性設定為this,也就是當前的Handler,這點很重要,之後的訊息處理會用到。最終是呼叫了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;
    }

首先檢查了改msg的target屬性是不是null,也就是是否設定了處理該msg的Handler,沒有設定Handler就丟擲異常,同一個msg不能進佇列兩次,否則丟擲異常
通過了上邊兩項檢查後,設定msg已使用,設定msg的處理事件,為msg的when屬性賦值

msg.markInUse();
msg.when = when;

然後根據msg的when屬性和Message佇列的情況,將該msg插入到佇列合適的位置

Handler訊息的輪詢處理

訊息通過Handler的post和send方法加入了handler的訊息佇列MessageQueue,那麼訊息佇列中的訊息是如何被依次處理的呢?是通過Looper,Handler有兩個重要的屬性

final MessageQueue mQueue;
final Looper mLooper;

在上邊建構函式的分析中,看到mQueue是如何被賦值的

mQueue = mLooper.mQueue;

即Handler的訊息佇列就是Looper的訊息佇列,因此訊息的輪詢處理是交給了Looper。一個Looper和一個執行緒繫結,要想使一個執行緒具備Looper的能力(輪詢訊息佇列),需要線上程run方法開頭呼叫

private static void prepare(boolean quitAllowed)

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

在prepare中通過ThreadLocal執行緒本地變數將Looper繫結到了呼叫prepare的執行緒。然後要想使執行緒具有輪詢能力,需要線上程run方法末尾呼叫

public static void 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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        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;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

可見在loop方法中通過死迴圈處理訊息佇列中的訊息

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的target屬性的dispatchMessage方法。
而target是如何被賦值的呢?是在Handler呼叫post或者send傳送訊息的時候被賦值的,被賦值為傳送此訊息的handler

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        ...

因此訊息的處理又回到了Handler執行緒,通過Handler的dispatchMessage方法回撥處理訊息

Handler處理訊息

在Handler所線上程,通過類似責任鏈模式的方式,處理該訊息

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

相關文章