[Android]從源碼中探討Handler機制

來源:互聯網
上載者:User

先來看一段代碼:

Thread thread = new Thread() {public void run() {//子線程中發送訊息給主線程Message msg = new Message();msg.what = 200;msg.obj = param;msg.arg1 = 3;handler.sendMessage(msg);};};Handler handler = new Handler() {public void handleMessage(Message msg) {//主線程接收到訊息,更新UI};};

這段代碼熟悉嗎?
在Android中,有兩種線程:主線程(UI線程,不允許做耗時的操作)和子線程(非UI線程,不允許操作介面元素)。而Handler是實現子線程與UI線程之間通訊的橋樑。那到底這是怎麼實現的呢?帶著這個疑問,我們去Android源碼中尋找答案吧!

先看一下Handler的構造方法:
public Handler() {    this(null, false);}public Handler(Callback callback, boolean async) {    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;}

構造方法中初始化了以下的兩個變數:
final MessageQueue mQueue;//訊息佇列(鏈表結構,下面會分析到)final Looper mLooper;//可理解為訊息處理器
而MessageQueue是Looper的成員屬性。


下面跟蹤Handler的sendMessage(msg)方法進去,可看到這個方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {    MessageQueue queue = mQueue;    if (queue == null) {}    return enqueueMessage(queue, msg, uptimeMillis);}private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {     //注意這一行,將Handler自已賦值給了Message的target屬性,下面的析中會用到    msg.target = this;    if (mAsynchronous) {        msg.setAsynchronous(true);    }    return queue.enqueueMessage(msg, uptimeMillis);}

我們再看一下MessageQueue的enqueueMessage(msg,when)方法的實現:
boolean enqueueMessage(Message msg, long when) {    synchronized (this) {        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類中是按鏈表的方式儲存的,MessageQueue類中以成員屬性變數mMessages記錄了一個排在最前面的訊息 ,每當有新訊息插入時,根據時間(when)的先後順序重新排列,時間最早的在最前面。

我們在Looper類的注釋中,找到下面這樣的範例程式碼:
class LooperThread extends Thread {  public Handler mHandler;  public void run() {      Looper.prepare();      mHandler = new Handler() {          public void handleMessage(Message msg) {              // process incoming messages here          }      };      Looper.loop();  }}

這個樣本可在直接在我們平常開發中使用。注意到,這裡也建立了一個Handler執行個體,就像我們常在Activity中建立一樣。我們知道,Activity是運行在UI線程中的,而UI線程也是一個線程,所以,我們猜想,Activity中建立Handler執行個體跟在這個樣本的線程中建立是一樣的效果。
我們還發現樣本中,Handler的建立的前後分別調用了兩個靜態方法:
Looper.prepare();Looper.loop();
這裡面大有學問,在繼續往下分析之前,我們再大膽猜測UI線程載入Activity的過程的前後也調用了這兩個方法。
public static void prepare() {    prepare(true);}//設定當前線程私人的Looper對象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對象static final ThreadLocal sThreadLocal = new ThreadLocal();//擷取當前線程私人的Looper對象public static Looper myLooper() {    return sThreadLocal.get();}

prepare()方法實際上是為當前線程建立了自己私人的Looper對象,連同它的屬性MessageQueue訊息佇列也是當前線程私人的。其他線程可以有他們自己的那個Looper,但不可以訪問當前線程的Looper。

Looper.loop();用於線上程中不斷地處理MessageQueue訊息佇列中的訊息,看一下它的實現代碼:
/** * 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;    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.recycle();    }}

方法實現中,用一個死迴圈不斷地提取MessageQueue隊列中的訊息,然後調用訊息的Target去分發這個訊息。Target是什麼? 正是上面分析到的Handler的enqueueMessage(...)方法中所設定的Handler自已本身。

再看一下Handler類的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)方法了。

下面總結一下:

在多線程的環境中,主線程和子線程之間互動是通過一個鏈表結構的訊息佇列(MessageQueue),子線程只管往裡面放入訊息(Message),訊息是按時間的先後順序排列的,主線程用一個訊息處理器(Looper)不斷地逐個逐個地處理掉訊息。

 

@容新華技術部落格 - http://blog.csdn.net/rongxinhua - 原創文章,轉載請註明出處

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.