源碼分析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()方法知道這個MessageQueueHandler中的一個成員。它是在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引用。所以每個線程都有一個消息隊列

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

Handler類圖

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()方法中有一個無限循環,不停地讀取調用MessageQueuenext()方法。當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對象,而通過loop()啟動一個循環不停地讀取隊列中Message。但是Android系統是什么時候啟動了主線程的消息循環呢?

要理解這一點就必須進入Android應用程序的入口ActivityThreadmain方法。

public static void main(String[] args) {
    ...

    Looper.prepareMainLooper();

    ...
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看出main方法中先后執行了Looper.prepareMainLooper()方法和Looper.loop()方法。正常情況下main方法不會退出,只有loop()方法發生異常后將會拋出RuntimeException

0x07 Looper.prepareMainLooper()

/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

prepareMainLooper()方法其實是調用了prepare()方法。

當我們啟動應用時系統就調用了prepareMainLooper()並在主線程中綁定了一個Looper對象。

這時候我們回過來看看一開始的Handler使用方式。在主線程中我們創建了Handler對象,在Handler構造函數中初始化了Looper(即獲取到了綁定在主線程中的Looper對象)。當在子線程MyThread中通過mHandler.sendMessage(msg)方法發送一個消息時就把Message放在與主線程綁定的MessageQueue中。這樣在子線程中使用Handler就實現了消息的通信。

可以簡單的使用以下類圖表示,每個線程都由一個Handler,每個Handler都是與當前所在線程的Looper綁定

線程Handler模型

0x08 主線程是否會阻塞

0x06中知道在ActivityTheadmain方法中啟動了一個死循環。那主線程是不是就一直阻塞在這里呢?其實不然。可以看到ActivityThread類里面有一個自定義的Handler對象mH,在這里對象中handleMessage()回調中定義了Activity的各種交互如管理Activity生命周期,啟動service,顯示window等,都是通過Handler進行處理的。同時可以看出只有當應用退出EXIT_APPLICATION之后才回調用Looper.quit()停止消息循環。

public void handleMessage(Message msg) {
    ...
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            ...
            handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        } break;
        ...
        case PAUSE_ACTIVITY: {
            ...
            handlePauseActivity((IBinder) args.arg1, false,
                    (args.argi1 & USER_LEAVING) != 0, args.argi2,
                    (args.argi1 & DONT_REPORT) != 0, args.argi3);
            ...
        } break;

        ...
        case SHOW_WINDOW:
            ...
            handleWindowVisibility((IBinder)msg.obj, true);
            ...
            break;
        ...
        case EXIT_APPLICATION:
            if (mInitialApplication != null) {
                mInitialApplication.onTerminate();
            }
            Looper.myLooper().quit();
            break;
        ...
    }
    ...
}

0x09 總結

當創建Handler時將通過ThreadLocal在當前線程綁定一個Looper對象,而Looper持有MessageQueue對象。執行Handler.sendMessage(Message)方法將一個待處理的Message插入到MessageQueue中,這時候通過Looper.loop()方法獲取到隊列中Message,然后再交由Handler.handleMessage(Message)來處理。

微信關注我們,可以獲取更多

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM