深入理解Handler、Looper、Messagequeue

轉載請註明出處:http://blog.csdn.net/vnanyesheshou/article/details/73484527

本文已授權微信公衆號 fanfan程序媛 獨家發佈 掃一掃文章底部的二維碼或在微信搜索 fanfan程序媛 即可關注

上一篇總結了一下Handler的基本用法,但是對於其原理並不太清楚,這篇主要分析下其內部的原理。看一下其源碼是怎麼回事,從源碼的角度理解Handler機制,Handler、Looper、MessageQueue之間的關係。


1 簡介

先對這幾個類做一下簡單介紹。
Handler
線程間通信的方式,主要用來發送消息及處理消息。
Looper
爲線程運行消息循環的類,循環取出MessageQueue中的Message;消息派發,將取出的Message交付給相應的Handler。
MessageQueue
存放通過Handler發過來的消息,遵循先進先出原則。
Message:消息,線程間通信通訊攜帶的數據。

例如後臺線程在處理數據完畢後需要更新UI,則可發送一條包含更新信息的Message給UI線程


2 Looper

源碼路徑:frameworks/base/core/java/android/os/Looper.java
Looper主要工作:

  • 自身實例的創建,創建消息隊列,保證一個線程中最多有一個Looper實例。
  • 消息循環,從消息隊列中取出消息,進行派發。

Looper用於爲線程運行消息循環的類,默認線程沒有與它們相關聯的消息循環;如果要想在子線程中進行消息循環,則需要在線程中調用prepare(),創建Looper對象。然後通過loop()方法來循環讀取消息進行派發,直到循環結束。

程序中使用Looper的地方:

  1. 主線程(UI線程)
    UI線程中Looper已經都創建好了,不用我們去創建和循環。
  2. 普通線程
    普通線程中使用Looper需要我們自己去prepare()、loop()。
    看一下普通線程中創建使用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();
   }
}

這段代碼是Looper源碼註釋中給的典型列子,主要步驟:

  1. Looper 準備,(Looper實例創建);
  2. 創建發送消息、處理消息的Handler對象;
  3. Looper開始運行。

印象中在UI線程沒有出現過Looper相關的東東,這是因爲UI線程中會自動創建Looper對象並進行消息循環,我們不再需要調用Looper.prepare()和Looper.loop(),但是在子線程中如果想要創建使用Handelr則需要向如上所示。
我們通過源碼看一下Looper實例創建的方法:

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

Looper構造方法是私有的,只能通過prepare()進行創建Looper對象。prepare()會調用私有方法prepare(boolean quitAllowed)。
第6行 sThreadLocal爲ThreadLocal類型變量,用來存儲線程中的Looper對象。
prepare方法中首先判斷sThreadLocal是否存儲對象,如果存儲了則拋出異常,這是因爲在同一個線程中Loop.prepare()方法不能調用兩次,也就是同一個線程中最多有一個Looper實例(當然也可以沒有,如果子線程不需要創建Handler時)。
該異常應該許多朋友都遇見過,如在UI線程中調用Looper.prepare(),系統會替UI線程創建Looper實例,所以不需要再次調用prepare()。

接着看Looper的構造器:

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

在構造器中,創建了一個MessageQueue消息隊列;然後獲取當前的線程,使Looper實例與線程綁定。
由prepare方法可知一個線程只會有一個Looper實例,所以一個Looper實例也只有一個MessageQueue實例。但這並不代表一個線程只能有一個MessageQueue實例,這是爲什麼呢?很簡單,我們可以自己new 一個MessageQueue實例就可以了,但這個MessageQueue並不是該線程中Handelr對應的消息隊列。

接着看Looper的消息循環:

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //通過Looper實例獲取消息隊列
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    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
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        //將消息派發給target屬性對應的handler,調用其dispatchMessage進行處理。
        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            //log
        }
        msg.recycle();
    }
}

loop()函數是靜態的,所以它只能訪問靜態數據。
第2行myLooper()函數也是靜態的,其代碼如下

return sThreadLocal.get()

獲取sThreadLocal存儲的Looper實例,如果爲空則拋出異常,這也說明loop()方法必須在prepare()方法之後才能調用。
第7行 通過Looper對象獲取消息隊列。
然後進行消息循環,從隊列中獲取消息,把消息交給msg的target的dispatchMessage方法進行處理,也就是交給handler進行處理,這個稍後說Handler時再細說。
然後調用msg.recycle(); 釋放msg。

Looper.loop()是給死循環,那如何終止消息循環呢?我們可以調用Looper的quit方法或quitSafely方法。
quit方法作用是把MessageQueue消息池中所有的消息全部清空,無論是延遲消息還是非延遲消息。
quitSafely只會清空MessageQueue消息隊列中所有的延遲消息,並將消息池中所有的非延遲消息派發出去讓Handler去處理,quitSafely相比於quit方法安全之處在於清空消息之前會派發所有的非延遲消息。

總結:

  1. UI線程會自動創建Looper實例、並且調用loop()方法,不需要我們再調用prepare()和loop().
  2. Looper與創建它的線程綁定,確保一個線程最多有一個Looper實例,同時一個Looper實例只有一個MessageQueue實例。
  3. loop()函數循環從MessageQueue中獲取消息,並將消息交給消息的target的dispatchMessage去處理。如果MessageQueue中沒有消息則獲取消息可能會阻塞。
  4. 通過調用Looper的quit或quitsafely終止消息循環。

3 Handler

源碼路徑:frameworks/base/core/java/android/os/Handler.java
Handler主要職責:

  1. 發送消息給MessageQueue(消息隊列);
  2. 處理Looper派送過來的消息;
    我們使用Handler一般都要初始化一個Handler實例。看下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;
}

第8行 Looper.myLooper();獲取當前線程保存的Looper實例,如果當前線程沒有Looper實例則會拋出異常。這也就是說在線程中應該先創建Looper實例(通過Looper.prepare()),然後纔可以創建Handler實例。
第13行 獲取Looper實例所保存的MessageQueue。之後使用Handler sendMesage、post都會將消息發送到該消息隊列中。保證handler實例與該線程中唯一的Looper對象、及該Looper對象中的MessageQueue對象聯繫到一塊。

3.1sendMessage

接着看一下平常使用Handler發送消息,先看sendMessage的流程:

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}

sendMessage最終調用到enqueueMessage函數,接着看下enqueueMessage。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
     msg.target = this;
     if (mAsynchronous) {
         msg.setAsynchronous(true);
     }
     return queue.enqueueMessage(msg, uptimeMillis);
 }

在enqueueMessage中,首先設置msg的target屬性,值爲this。之前在Looper的loop方法中,從消息隊列中取出的msg,然後調用msg.target.dispatchMessage(msg);其實也就是調用當前handler的dispatchMessage函數。
然後調用queue的dispatchMessage方法,將Handler發出的消息,保存到消息隊列中。

3.2 post

看一下post方法

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

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

post方法中調用getPostMessage方法,創建一個Message對象,設置此Message對象的callback屬性爲創建Runnable對象。
然後調用sendMessageDelayed,最終和sendMessage一樣,都是調用到sendMessageAtTime。調用enqueueMessage方法,將此msg添加到MessageQueue中。
也就是post(Runnable r) 並沒有創建線程。其run方法是在Handler對應的線程中運行的。

3.3 dispatchMessage

這裏主要說下handler是如何處理消息的。在Looper.loop方法中通過獲取到的msg,然後調用msg.target.dispatchMessage(msg);也就是調用handler的dispatchMessage方法,看下Handler中dispatchMessage源碼

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

在dispatchMessage方法中首先判斷msg的callback屬性,如果不爲空則調用handleCallback函數,
handleCallback函數如下:

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

handleCallback函數中messag.callback也就是我們傳的Runnable對象,也就是調用Runnable對象的run方法。
如果msg.callback屬性爲空,判斷Handler屬性mCallback是否爲空, 不爲空則讓mCallback處理該msg。
mCallback爲空則調用Handler的handleMessage,這就是我們創建Handler對象時一般都實現其handleMessage方法的原因。


4 MessageQueue

源碼路徑:frameworks/base/core/java/android/os/MessageQueue.java
MessageQueue 消息隊列:

  • enqueueMessage將消息加入隊列
  • next從隊列取出消息
  • removeMessage移除消息

MessageQueue內部是如何管理這些消息隊列的就先不說了,之後又空再好好分析一下。


5 總結

本文分析了下Handler、Looper、MessageQueue之間的聯繫,及handler進程間通信的原理。瞭解到Handler不僅僅可以更新UI,也可以在一個子線程中去創建一個Handler,然後使用這個handler實例在任何其他線程中發送消息,最終處理這些消息的代碼都會在你創建Handler實例的線程中運行。

歡迎掃一掃關注我的微信公衆號,定期推送優質技術文章:

這裏寫圖片描述

發佈了129 篇原創文章 · 獲贊 270 · 訪問量 97萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章