Android中利用Handler實現訊息的分發機制(一)

來源:互聯網
上載者:User

標籤:android   handler   looper   messagequeue   

上一篇文章,我們講到在調用Handler的sendMessage方法時,最終我們會進入到一個叫 sendMessageAtTime的方法,如下:

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

在這裡,我們看到了MessageQueue和enqueueMessage等變數跟方法,我們可以想到,在Handler的實現的機制中,一定存在著一個訊息佇列,而它存放了我們建立的眾多訊息(Message)對象。

從這裡,我們就會開始去探尋隱藏在 Handler對象後面的那一些我們想知道的實現機制了。

首先,我們還是從 Handler的建立開始說起,在上一篇文章,我們是通過 new Handler的方法來建立的,代碼如下:

    private Handler mHandler = new Handler() {        public void handleMessage(Message msg) {            switch (msg.what) {            case MSG_ID_1:                Log.v("Test", "Toast called from Handler.sendMessage()");                break;            case MSG_ID_2:                String str = (String) msg.obj;                Log.v("Test", str);                break;            }                    }    };

顯然,我們要去看一下Handler的建構函式,如下:

    public Handler() {        this(null, false);    }    public Handler(Callback callback) {        this(callback, false);    }    public Handler(Looper looper) {        this(looper, null, false);    }        public Handler(Looper looper, Callback callback) {        this(looper, callback, false);    }       public Handler(boolean async) {        this(null, async);    }    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;    }        public Handler(Looper looper, Callback callback, boolean async) {        mLooper = looper;        mQueue = looper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

我們可以看到,真正實現的建構函式,其實只有下面兩個,如下:

    public Handler(Callback callback, boolean async) {        ....    }        public Handler(Looper looper, Callback callback, boolean async) {        ...    }

這兩個的差別就在於是否有參數Looper,而Looper是一個線程相關的對象。

何謂線程相關的變數?就是線程間不能共用的對象,只在本線程內有作用的對象。

那麼Looper對象的作用是什嗎?

從我個人的理解,Looper類就是對MessageQueue的封裝,它主要做的是兩件事:

1)構造Looper對象,初始化MessageQueue,我們可以從其建構函式看到:

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


顯然,MessageQueue正是在建立Looper的時候初始化的。

我們還注意到,這個建構函式是private的,而它則是被Looper.prepare方法調用的,如下:

    public static void prepare() {        prepare(true);    }    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));    }

可以看到,Loop對象被建立之後,會被放到ThreadLocal變數中,而ThreadLocal正是線程局部變數,這說明了關於Looper的一個特性:

每一個線程中都只能有一個Looper對象。

2)調用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中獲得訊息,然後通過msg.target.dispatchMessage(msg)方法調用,處理訊息,最後將訊息進行回收。

在這裡,我們先不關心dispatchMessage方法,我們先跑一下題,看一下recycle方法裡面做了什麼事吧,如下:

    private static Message sPool;    private static int sPoolSize = 0;    private static final int MAX_POOL_SIZE = 50;    /**     * Return a new Message instance from the global pool. Allows us to     * avoid allocating new objects in many cases.     */    public static Message obtain() {        synchronized (sPoolSync) {            if (sPool != null) {                Message m = sPool;                sPool = m.next;                m.next = null;                sPoolSize--;                return m;            }        }        return new Message();    }        public void recycle() {        clearForRecycle();        synchronized (sPoolSync) {            if (sPoolSize < MAX_POOL_SIZE) {                next = sPool;                sPool = this;                sPoolSize++;            }        }    }

從上面的代碼中,我們可以看到,Message對象本身有一個next的欄位指向另外一個Message,也就是說,可以通過鏈表的方式將眾多的Message給串起來,變成一個鏈表的訊息池sPool。

而在這裡,當調用recycle方法,就會將當前Message對象,先clearForRecycle之後,再添加到 sPool的頭部中,而當我們通過Message的obtain方法的時候,我們其實也是從sPool中拿 出一個空的Message對象。

相信看到這裡,大家就瞭解了上一篇文章中我說,為什麼建議大家使用Message.obtain方法去擷取訊息對象了吧。

接下來,我們再回到正題上。

從上面關於Handler的建立和關於Looper的描述中,我們可以得出這樣一個結論:

在每一個線程中,如果我們要建立Handler,那麼此線程中就必須有一個Looper對象,而這個Looper對象中又封裝了一個MessageQueue對象來對Message進行管理。

所以,如果我們要在一個新線程中使用handler的話,我們就必須通過調用Loop.prepare()方法,為此線程建立一個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();      }  }

當然,只有理論當然是不行的,我們還是得通過一個例子來看一下具體的效果,如下:

public class MainActivity extends ActionBarActivity {    private static final int MSG_ID_1 = 1;    private static final int MSG_ID_2 = 2;    class LooperThread extends Thread {        public Handler mHandler;        public void run() {            Looper.prepare();                mHandler = new Handler() {                                public void handleMessage(Message msg) {                    Log.v("Test", "Id of LooperThread : " + Thread.currentThread().getId());                    switch (msg.what) {                    case MSG_ID_1:                        Log.v("Test", "Toast called from Handler.sendMessage()");                        break;                    case MSG_ID_2:                        String str = (String) msg.obj;                        Log.v("Test", str);                        break;                    }                }            };            Looper.loop();        }    }        protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.v("Test", "Id of MainThread : " + Thread.currentThread().getId());                LooperThread looperThread = new LooperThread();        looperThread.start();                while(looperThread.mHandler == null){                                }                Message message = Message.obtain();        message.what = MSG_ID_1;        looperThread.mHandler.sendMessage(message);        Message msg2 = Message.obtain();        msg2.obj = "I'm String from Message 2";        msg2.what = MSG_ID_2;        looperThread.mHandler.sendMessage(msg2);    }}

對應的結果如下:

10-27 16:48:44.519: V/Test(20837): Id of MainThread : 110-27 16:48:44.529: V/Test(20837): Id of LooperThread : 6842110-27 16:48:44.529: V/Test(20837): Toast called from Handler.sendMessage()10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 6842110-27 16:48:44.529: V/Test(20837): I'm String from Message 2

那其實在這裡有一個問題,為什麼我們平常可以直接去建立Handler對象,而不需要去調用UI線程的Looper.prepare和loop等方法呢?

當然,這肯定也是需要的,只是這一步是由Android系統幫我們做了,所以預設的主線程就不再需要去做這些初始化。

好了,這一篇文章,我們就瞭解了關於線程,Looper,Handler, MessageQueue 和 Message 等之間的一些關聯,而主要是對於Looper對象的認識。

結束。

Android中利用Handler實現訊息的分發機制(一)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.