Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中利用Handler實現消息的分發機制(一)

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

編輯:關於Android編程

上一篇文章,我們講到在調用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 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 : 1
10-27 16:48:44.529: V/Test(20837): Id of LooperThread : 68421
10-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 : 68421
10-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對象的認識。

結束。

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