天天看點

源碼分析Android Handler是如何實作線程間通信的

源碼分析Android Handler是如何實作線程間通信的

Handler作為Android消息通信的基礎,它的使用是每一個開發者都必須掌握的。開發者從一開始就被告知必須在主線程中進行UI操作。但Handler是如何實作線程間通信的呢?本文将從源碼中分析Handler的消息通信機制。

0x00 Handler使用

首先看看我們平時是如何使用的

Handler

的。先看看以下代碼

//定義Handler
Handler mHandler = new Handler(){
  public void handleMessage(Message msg){
    switch(msg.what){
      case UPDATE_UI:
        updateUI(msg);
        break;
    }
  }
};
class MyThread extends Thread{
  public void run(){
    //do same work!
    ...
    //send message
    Message msg = mHandler.obtainMessage(UPDATE_UI);
    mHandler.sendMessage(msg);
  }
}

private void updateUI(Message msg){
  //update UI
}
           

在子線程中

sendMessage(Message)

發送消息,然後在Handler的

handleMessage(Message)

接收消息,執行更新UI操作。那麼

Handler

是如何把消息從

MyThread

傳遞到

MainThread

中來呢?我們從

sendMessage()

開始慢慢揭開它的面紗。

0x01 sendMessage(Message)

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

我們發現調用

sendMessage()

方法最後都走到

enqueueMessage()

這個方法,一開始就把目前

Handler

執行個體賦給了

Message.target

的屬性裡面,後面可以知道這個

target

是用來執行處理函數回調的。

enqueueMessage

方法是把

Message

資訊放入到一個

MessageQueue

的隊列中。顧名思義

MessageQueue

就是消息隊列。從

sendMessageAtTime()

方法知道這個

MessageQueue

Handler

中的一個成員。它是在

Handler

的構造函數中通過

Loopger

對象來初始化的。

0x02 Handler構造函數

public Handler(Callback callback, boolean async) {
    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());
        }
    }

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

這時候我們腦海知道建立

Handler

的時候,同時也建立了

Looper

執行個體和

MessageQueue

引用(

MessageQueue

對象其實是在

Looper

中構造的)。

Looper

是何物呢?簡單地說就是消息循環,這個我們稍後會分析。

0x03 enqueueMessage(MessageQueue)

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(TAG, 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;
}
           

MessageQueue

中可以看到這個入列方法中有一個

for

循環就是把目前的需要處理

Message

放到隊列的合适位置。因為需要處理的

Message

對象都有一個開始處理的時間

when

,這個隊列是按照

when

排序的。

至此,

Handler

調用

sendMessage()

方法後就把

Message

消息通過

enqueueMessage()

插入

MessageQueue

隊列中。

而這個

MessageQueue

是在

Looper

中維護的。

0x04 prepare()建立Looper

在0x02中我們知道建立

Handler

時就使用靜态方法

Looper.myLooper()

得到目前線程的

Looper

對象。

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
           

sThreadLocal

是一個

ThreadLocal

類型的靜态變量。什麼時候會把

Looper

對象放在

sThreadLocal

中呢?通過

prepare()

方法。

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

繼續翻閱源碼知道

Looper

在構造函數中建立

MessageQueue

對象

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

prepare()

方法将一個

Looper

對象放在了靜态的

ThreadLocal

對象中。這個是一個與線程綁定的對象,且在記憶體中僅儲存了一份引用。

使用

ThreadLocal

對象這一點非常巧妙,也非常重要,這是線程間通信的基礎。即線上程中調用

prepare()

時就在該線程中綁定了

Looper

對象,而

Looper

對象中擁有

MessageQueue

引用。是以每個線程都有一個消息隊列。

這樣

Handler

Looper

MessageQueue

這幾個類關系大概就可以畫出來了。

0x05 啟動循環loop()

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the 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
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
          //執行處理消息的回調
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        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()

方法中有一個無限循環,不停地讀取調用

MessageQueue

next()

方法。當

next()

沒有傳回時就阻塞在這裡。當擷取到

MessageQueue

中的消息時,就執行了處理消息的回調函數

msg.target.dispatchMessage(msg)

前面0x01分析我們知道

msg.target

Handler

中的

enqueueMessage()

進行指派,即它指向目前的

Handler

執行個體。

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

執行

msg.target.dispatchMessage(msg)

後便走到了以下流程

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

這裡就是回調

handleMessage(msg)

函數處理消息的地方。

Handler

負責将

Message

入列,

Looper

則負責循環從

MessageQueue

中取出需要處理的

Message

并交由Handler來處理。

0x06 啟動主線程的消息循環

我們知道通過靜态方法

Looper.prepare()

建立了綁定目前線程的

Looper

對象,

繼續閱讀