Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android資訊 >> Android Handler Looper Message 詳細分析

Android Handler Looper Message 詳細分析

編輯:Android資訊

Android異步消息機制架構

Android異步消息處理架構,其實沒那麼復雜。簡單來說就是 looper 對象擁有 message queue ,並且負責從 message queue 中取出消息給 handler 來處理。同時 handler 又負責發送 message 給 looper ,由 looper 把 message 添加到 message queue 尾部。就一個圈兒。下面給出圖解,應該不難吧?

Android Handler Looper Message 詳細分析

所以很明顯 handler 和 looper 是來聯系在一起的。需要說明的是,多個 message 可以指向同一個 handler ,多個 handler 也可以指向同一個 looper 。

還有一點很重要,普通的線程是沒有 looper 的,如果需要 looper 對象,那麼必須要先調用 Looper.prepare() 方法,而且一個線程只能有一個 looper 。調用完以後,此線程就成為了所謂的 LooperThread ,若在當前 LooperThread 中創建 Handler 對象,那麼此 Handler 會自動關聯到當前線程的 looper 對象,也就是擁有 looper 的引用。

Looper

簡而言之, Looper 就是一個管理 message queue 的類。我們直接來看下源碼好了。

public class Looper {
    ......

    private static final ThreadLocal sThreadLocal = new ThreadLocal();

    final MessageQueue mQueue;//擁有的消息隊列

    ......

    /** Initialize the current thread as a looper.
 * This gives you a chance to create handlers that then reference
 * this looper, before actually starting the loop. Be sure to call
 * {@link #loop()} after calling this method, and end it by calling
 * {@link #quit()}.
 */
    //創建新的looper對象,並設置到當前線程中
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
    ·····
    /**
 * Return the Looper object associated with the current thread. Returns
 * null if the calling thread is not associated with a Looper.
 */
    //獲取當前線程的looper對象
    public static final Looper myLooper() {
        return (Looper)sThreadLocal.get();
    }

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

    ......
}

由源碼可知, looper 擁有 MessageQueue 的引用,並且需要調用 Looper.prepare() 方法來為當前線程創建 looper 對象。Android注釋很詳細的說明了這個方法。

This gives you a chance to create handlers that then reference

this looper, before actually starting the loop. Be sure to call

loop() after calling this method, and end it by calling

quit()

也就是說只用在調用完 Looper.prepare() 之後,在當前的線程創建的 Handler 才能用有當前線程的 looper 。然後調用 loop() 來開啟循環,處理 message .來簡單看一下 loop() 的源碼

public static void loop() {
        final Looper me = myLooper();//獲取looper對象
        if (me == null) {
            //若為空則說明當前線程不是LooperThread,拋出異常
            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;
            }

            // This must be in a local variable, in case a UI event sets the logger
            //打印log,說明開始處理message。msg.target就是Handler對象
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            //重點!!!開始處理message,msg.target就是Handler對象
            msg.target.dispatchMessage(msg);

            //打印log,處理message結束
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
            .....
        }
    }

很明顯,就是一個大的循環,不斷從消息隊列出取出消息。然後調用一個很關鍵的方法 msg.target.dispatchMessage(msg) 開始處理消息。 msg.target 就是 message 對應的 handler .其實我一直在重復文章開頭的概念。 looper 對象管理 MessageQueue ,從中取出 message 分配給對應的 handler 來處理。

在分析 msg.target.dispatchMessage(msg) 方法之前,先讓大家了解下 message 和 handler

Message

Message 就是一些需要處理的事件,比如訪問網絡、下載圖片、更新ui界面什麼的。 Message 擁有幾個比較重要的屬性。

  • public int what 標識符,用來識別 message
  • public int arg1,arg2 可以用來傳遞一些輕量型數據如int之類的
  • public Object obj Message 自帶的Object類字段,用來傳遞對象
  • Handler target 指代此 message 對象對應的 Handler

如果攜帶比價復雜性的數據,建議用 Bundle 封裝,具體方法這裡不講了。

值得注意的地方是,雖然 Message 的構造方法是公有的,但是不建議使用。最好的方法是使用 Message.obtain() 或者 Handler.obtainMessage() 能更好的利用循環池中的對象。一般不用手動設置 target ,調用 Handler.obtainMessage() 方法會自動的設置 Message 的 target 為當前的 Handler 。

得到 Message 之後可以調用 sendToTarget() ,發送消息給 Handler , Handler 再把消息放到 message queue 的尾部

Handler

public Handler(Looper looper, Callback callback, boolean async) {
       mLooper = looper;
       mQueue = looper.mQueue;
       mCallback = callback;  //handleMessage的借口
       mAsynchronous = async;
   }

上面是 Handler 構造器,由此可知,它擁有 looper 對象,以及 looper 的 message queue 。

要通過 Handler 來處理事件,可以重寫 handleMessage(Message msg) ,也可以直接通過 post(Runnable r) 來處理。這兩個方法都會在looper循環中被調用。

還記得剛在loop循環中處理信息的 msg.target.dispatchMessage(msg) 方法。現在我們來看下這個方法的源碼。

public void dispatchMessage(Message msg) {
    //注意!這裡先判斷message的callback是否為空,否則就直接處理message的回調函數
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //正是在這調用我們平常重寫handleMessage
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

沒錯,正是在這個方法中調用了 handleMessage(Message msg) 。值得注意的是,再調用 handleMessage(Message msg) 之前,還判斷了message的callback是否為空。這是為什麼?

很簡單,這就是 post(Runnable r) 方法也能用來處理事件的原因。直接看它的源碼。我就不贅述了。相信已經很清晰了。

public final boolean post(Runnable r)
{
    //獲取消息並發送給消息隊列
    return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    //創建消息,且直接設置callback
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

總結

現在一切都弄清楚了。先調用 Looper.prepare() 使當前線程成為 LooperThread , Looper.loop() 開啟循環之後,然後創建 message ,再由 Handler 發送給消息隊列,最後再交由 Handler 處理。下面是官方給出的LooperThread最標准的用法。

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

最後的最後,再提一下,平時在說的主線程也就是UIThread,也是一個LooperThread。這也是為什麼我們能在子線程中發送消息,然後在主線程中更新ui。因為 Handler 是在主線程創建的,所以 Handler 關聯的是主線程的 looper ,最後給一個更新ui的實例。

 private static Handler handler=new Handler();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.message_activity);  
    new Thread(new Runnable() {                    
        @Override
        public void run() {
            // tvTest.setText("hello word");
            // 以上操作會報錯,無法再子線程中訪問UI組件,UI組件的屬性必須在UI線程中訪問
            // 使用post方式設置message的回調
            handler.post(new Runnable() {                    
                @Override
                public void run() {
                    tvTest.setText("hello world");                        
                }
            });                                
        }
    }).start();
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved