Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android消息循環機制探討

Android消息循環機制探討

編輯:關於Android編程

怎樣理解Android中的Handler,MessageQueue、Runnable與Looper?

簡單來講,用一句話概括就是:
Looper不斷獲取MessageQueue中的一個Message(Runnable會包裝成Message),然後由Handler來處理。

這其實就是進程/線程跑起來的基礎,我們稱為消息循環處理機制。其實任何系統能夠跑起來的本質就是依賴於這樣一個機制。

所以我們第一個要搞清楚的問題是在Android中消息循環處理機制是怎麼搭建起來的?

線程是CPU調度的基本單位,也就是說線程相當於一個平台,我們利用這個平台做各種各樣的事情,顯然消息循環機制(Looper)也是在線程上完成的。所以我們首先看下在線程裡是怎麼使用Looper的?舉個簡單的例子:

class LooperThread extends Thread{
        @Override
        public void run() {
            Looper.prepare();
            Handler h = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
            Looper.loop();
        }
    }

從上面的代碼可以看出,Looper的使用是非常簡單的,就3個步驟。

第一步:Looper.prepare();
准備工作,看下源碼裡面做了什麼事情?

public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
}

private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

首先看下sThreadLocal對象是什麼東西?

static final ThreadLocal sThreadLocal = new ThreadLocal();

哦,原來是ThreadLocal對象,這裡ThreadLocal的意義在於為每個線程隔離Looper對象,也就是每個線程對應一個Looper對象,sThreadLocal就是用來統一管理每個線程的Looper對象,確保每個線程的Looper對象是獨立的。所以Looper.prepare();的作用在於為線程創建一個Looper對象,可以通過sThreadLocal.get()來獲得。從代碼裡可以看出,一個線程對應一個Looper對象。然後在Looper的構造器裡初始化了MessageQueue。

第二步:創建處理消息的Handler;
你肯猜到了,Handler和Looper存在某種關系,不然這個消息循環處理是怎麼連接起來的。所以我們從Handler的構造方法入手。

public 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 = null;
    }

從上面的代碼裡我們得到以下幾條信息:
1、在Handler裡通過Looper.myLooper()得到了線程的Looper對象;

public static Looper myLooper() {
        return sThreadLocal.get();
}

2、在Handler裡維護了一個MessageQueue mQueue,指向Looper對象裡的MessageQueue。
3、有一個回調接口對象 mCallback

好,現在Handler、Looper和MessageQueue算是有了一些聯系。

下面看第三步:Looper.loop();
前面兩步都是准備工作,這一步開始真正做事情了,我們來看下loop()方法裡的主要流程。

public static void loop() {
        Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        MessageQueue queue = me.mQueue;

        while (true) {
            Message msg = queue.next(); // might block
            if (msg != null) {
                if (msg.target == null) {
                    return;
                }
                msg.target.dispatchMessage(msg);

                msg.recycle();
            }
        }
    }

從上面可以看出,消息循環的機制就是從Looper對象中拿到消息隊列MessageQueue,然後不斷的從MessageQueue裡拿到消息,然後由handler(msg.target)去處理(dispatchMessage(msg))。

從創建Looper對象和Handler對象,到handler和Looper之間聯系起來,再到消息循環處理,就這樣,消息循序處理機制搭建起來了,線程跑起來了,整個流程我們似乎也明白了,但其實仔細想想,我們忽略了很多細節:

1、前面我們講了從消息隊列MessageQueue中取出消息message交由handler處理,但好像沒看到過消息是怎麼放進消息隊列的?Message和handler是怎麼聯系起來的也不清楚?

我們知道handler是用來處理消息的,其實handler還有另外一個作用—-發送消息:將Message壓入MessageQueue中。Handler發送消息用到的API分為兩個系列,Post系列和Send系列。
Post系列:
public final boolean post(Runnable r);
public final boolean postAtTime(Runnable r, long uptimeMillis);
…………
Send系列:
public final boolean sendMessage(Message msg);
public final boolean sendEmptyMessage(int what);
public final boolean sendMessageDelayed(Message msg, long delayMillis);
public boolean sendMessageAtTime(Message msg, long uptimeMillis);
……………
Post和Send兩個系列的共同點是它們都負責將某個消息壓入MessageQueue中;區別在於後者處理的函數參數直接是Message,而Post處理的參數是Runnable,將Runnable轉換成Message,再調用Send系列函數來執行下一步。
我們挑選第一個Post函數來分析下其流程:

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

首先通過方法getPostMessage(r)將Runnable對象封裝成Message ,再通過對應的send函數把它推送到MessageQueue中;

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

從getPostMessage(r)方法中我們可以知道以下幾點
1)Android系統會維護一個全局的Message池,當用戶需要使用Message時,可以通過obtain直接獲得,而不是自行創建。這樣的設計可以避免不必要的資源浪費。
2)Runnable對象賦給的Message的callback,在Message類中callback的定義如下:
Runnable callback;

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

SystemClock.uptimeMillis():從手機開機到現在的毫秒數,不包括睡眠時間。

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        return sent;
}

還記得mQueue嗎?在handler的構造器中將Looper裡的MessageQueue賦給了mQueue。
Message和handler也在這裡聯系起來了:msg.target = this;
最後queue.enqueueMessage(msg, uptimeMillis)將消息壓入隊列。

2、對於handler處理消息,上面我們是一筆帶過的,msg.target.dispatchMessage(msg); 那消息處理的流程到底是怎麼樣的呢?
在Handler類中對消息處理有兩個方法:
public void dispatchMessage(Message msg);//對Message進行分發
public void handleMessage(Message msg);//對Message進行處理
Looper從MessageQueue中取出一個Message後,首先會調用Handler. dispatchMessage進行消息派發,

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

首先判讀msg.callback是否為空,還記得這個嗎?前面提到過msg.callback就是Post系列傳入的Runnable對象,所以Post系列發送的消息msg.callback不為空,走handleCallback(msg),

private final void handleCallback(Message message) {
        message.callback.run();
}

直接調用run方法執行任務,消息處理結束。
Send系列發送的消息callback為空,所以走else,先判斷mCallback是否為空,mCallback對象是什麼?其實這是一個回調接口對象,接口裡也有一個handleMessage(msg)方法用來處理消息,

public interface Callback {
        public boolean handleMessage(Message msg);
}

這個對象是在Handler的構造器中傳入的,傳了就有,沒傳就沒有,作用在於有了回調接口來處理消息,就不用寫Handler的子類來實現handleMessage(Message msg)方法了。
如果mCallback為空,或者mCallback.handleMessage(msg)消息沒處理成功,就走Handler類中的handleMessage(msg)了,

public void handleMessage(Message msg) {
}

在Handler類中,這是一個空的方法,子類重寫這個方法去完成消息處理的邏輯。
講到這裡,你可能會很奇怪,Handler同時負責發送消息和處理消息,為什麼不直接操作,而是大費周章地先把Message壓入MessageQueue,再取出來處理?其實這體現了程序設計的一個良好的習慣,即“有序性”。如果世界沒有了秩序,那將亂的一塌糊塗,程序也是如此。

3、我們知道,調用Looper.loop()後消息循環處理就開始了,那循環什麼時候退出了?
從loop()方法裡可以看到有一種情況可以退出循環,就是當msg.target == null ;時,但是上面我們講過msg.target的賦值是在sendMessageAtTime(Message msg, long uptimeMillis)方法裡,而不管是Post還是Send發送消息都會走到這個方法,從而給msg.target賦值,所以正常情況下消息處理循環是不會退出的,怎麼辦了?其實在Looper類中有一個quit()方法,我們看下裡面做了什麼事情?

public void quit() {
        Message msg = Message.obtain();
        mQueue.enqueueMessage(msg, 0);
    }

在這裡面直接往MessageQueue中壓入一條消息,這樣msg.target就為空了,當循環到這條消息時,就退出了循環,所以如果你想終止消息處理循環,請調用這個方法即可。

現在,我們基本上理清了Android中的Handler,MessageQueue、Runnable與Looper之間的關系和它們之間的流程機制了。每個Thread只對應一個Looper;每個Looper只對應一個MessageQueue。把一個Thread比作一座城市,Looper是城市的戶籍名稱;MessageQueue是這座城市的唯一地標性建築;而Hander就是人口,在一座城市,可以有本地人口,也可以有外地人口,但每個人都有戶籍,標識他屬於哪座城市;Message就是貨物,不區分屬地,攜帶在人的身上,在城市或城市間流通。
最後提一點,在Looper類中還有這樣兩個方法:

public static void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        myLooper().mQueue.mQuitAllowed = false;
}

public synchronized static Looper getMainLooper() {
        return mMainLooper;
}

這兩個方法就是我們熟悉的UI主線程專用的方法了,
new Handler(getMainLooper())就代表在主線程運行了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved