Android訊息框架速記

李納斯小盒發表於2018-05-21

Android開發知識筆記 onGithub

涉及到的4個類:

  • Handler
  • Looper
  • ThreadLocal
  • MessageQueue

1.ThreadLocal

每個執行緒有一些和自己相關的變數,ThreadLocal的作用就是儲存這些變數的。所有的變數是通過內部的靜態類Value儲存的。雖然,執行緒都是通過訪問相同的ThreadLocal,但是每個執行緒儲存的變數是分開的:

public void set(T value) {    
      Thread currentThread = Thread.currentThread();    
      Values values = values(currentThread);    
      if (values == null) {        
            values = initializeValues(currentThread);    }    
      values.put(this, value);
}
複製程式碼

上面的set方法中,values方法返回當前執行緒的localValues成員變數:

/** 
  * Gets Values instance for this thread and variable type. 
  */
Values values(Thread current) {    
      return current.localValues;
}
複製程式碼

那麼執行緒內部的localValues是什麼?

public class Thread implements Runnable {
    /** 
      * Normal thread local values. 
      */
    ThreadLocal.Values localValues;
    /*省略若干程式碼*/
}
複製程式碼

可以看到,Thread中的localValues是定義在ThreadLocal中執行緒本地的變數。如果在 values ()方法取得null值,就執行initializeValues方法。 initializeValues是如何實現的呢?

Values initializeValues(Thread current) {    
      return current.localValues = new Values();
}
複製程式碼

然後將value的值放在當前執行緒的的localValues裡。這樣,雖然看起來訪問的是用一個ThreadLocal,但是得到的值卻是根據執行緒而不同的。

注:不同sdk中ThreadLocal內部的實現時不一樣的,比如在6.0的版本實現的方式就不是上面的方式,但是原理還是一樣的

舉個例子

public class JsonTestMetaData {
    public String json_str;
}

public class MainActivity extends Activity {

    ThreadLocal<JsonTestMetaData> local = new ThreadLocal<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
       
        JsonTestMetaData data = new JsonTestMetaData();
        data.json_str = "main_thread";
        local.set(data);

        Log.e(TAG, local.get().json_str);

        new Thread(new Runnable() {
            @Override
            public void run() {
                JsonTestMetaData data = new JsonTestMetaData();
                data.json_str = "other_thread";
                local.set(data);
                Log.e(TAG, local.get().json_str);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                if (local.get() != null) {
                    Log.e(TAG, local.get().json_str);
                } else {
                    Log.e(TAG, "local.get() is null");

                }
            }
        }).start();


        Log.e(TAG, local.get().json_str);

    }
}
複製程式碼

得到的結果:

01-09 14:28:36.410 29303-29303/com.sparkfengbo.app.javabcsxtest E/MainActivity: main_thread
01-09 14:28:36.412 29303-29303/com.sparkfengbo.app.javabcsxtest E/MainActivity: main_thread
01-09 14:28:36.412 29303-29331/com.sparkfengbo.app.javabcsxtest E/MainActivity: other_thread
01-09 14:28:36.413 29303-29332/com.sparkfengbo.app.javabcsxtest E/MainActivity: local.get() is null
複製程式碼

2.MessageQueue

MessageQueue是一個訊息佇列,包含成員變數Message mMessages;,可以理解成連結串列的頭部。儲存的形式不是佇列,而是單連結串列。 內部包含5個native方法:

private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native static void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsIdling(long ptr);
複製程式碼

底層還是通過native程式碼完成的。

在Java層面,

主要是next方法,獲得下一個訊息;

enqueueMessage 將訊息插入到佇列中。

3.Looper

Looper是做什麼的?Looper的職能是為一個執行緒建立MessageQueue,繫結到這個執行緒,為此執行緒執行訊息迴圈。

Looper內部包含MessageQueue和Thread的引用

MessageQueue在prepare方法中建立,在loop方法開始迴圈。

Java層的Looper和MessageQueue有在C++對應的類,分別是Looper(Native)和NativeMessageQueue類

執行緒預設是沒有looper的,除非你線上程呼叫prepare方法,然後才能執行loop方法才能進行訊息處理。

prepare做了什麼呢?

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));}
複製程式碼

你可以看到prepare()方法只能呼叫一次。在最後會建立一個Looper放在ThreadLocal裡儲存。 Looper是如何建立的呢?

private Looper(boolean quitAllowed) {    
      mQueue = new MessageQueue(quitAllowed);    
      mThread = Thread.currentThread();
}
複製程式碼

可以看到,構造方法是私有的,新建立了一個MessageQueue,mThread就是當前執行緒。 那麼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.");    
        }    
        final MessageQueue queue = me.mQueue;    
        for (;;) {        
              Message msg = queue.next();       
              msg.target.dispatchMessage(msg);     
              msg.recycleUnchecked();    
        }
}
複製程式碼

可以看到,通過一個無限迴圈,不停的在訊息佇列中拿訊息,將訊息分發到指定的地方。

Message的target其實就是Handler

所以,在你寫的執行緒中,可以這樣使用:

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();
     }
}
複製程式碼

不過上述的方式太low,在程式碼中也不方便,可以這樣寫:

HandlerThread

HandlerThread thread  = new HandlerThread("new_handler_thread");
Handler handler = new Handler(thread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
});
複製程式碼

HandlerThread繼承自Thread,自身會建立一個Looper。

關於 HandlerThread可參考 Android HandlerThread 完全解析

大多數和訊息迴圈的互動都是通過Handler去完成的,就像你在Handler那部分看到的那樣。記得在你不再執行訊息迴圈的時候呼叫Looperquit方法。

4.Handler

Handler能夠傳送和處理和MessageQueue關聯的 MessageRunnable。每個Handler和一個單獨的執行緒關聯,這個執行緒就是你建立這個Handler的時候所在的執行緒,需要處理的MessageQueue也是這個執行緒的MessageQueue

請看:

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

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
複製程式碼

Handler有很多個建構函式,最終都會呼叫到這個建構函式。你可以看到Handler中的的Looper成員,就是通過Looper的靜態方法myLooper得到的,myLooper是幹啥的?你可以看Looper的內容,在上面程式碼中得到了一個和這個執行緒關聯的Looper。如果 mLooper成員是null,那麼就丟擲異常。你可能會問,我在activity中隨便建立handler啊,沒有呼叫Looper.myLooper()方法。那是因為當你的應用執行的時候,Android已經通過Looper的靜態方法prepareMainLooper建立了,這個方法只能執行一次,否則就會丟擲異常了。這個Looper適合執行緒繫結的,你再看看mQueue,是從mLooper中拿到的。

呼叫的順序如下:

  • Message的callback是否是null?不是呼叫callback的run方法(其實這裡Message的callback是一個Runnable物件,通過Handler的post方法傳遞)
  • Handler的callback是否是null?不是呼叫callback。這裡的callback的型別是Handler.Callback()
  • 最終呼叫Handler的handleMessage的方法。
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
複製程式碼

5. MessageQueue.IdleHandler

簡而言之,就是在looper裡面的message暫時處理完了,這個時候會回撥這個介面,返回false,那麼就會移除它,返回true就會在下次message處理完了的時候繼續回撥

相關文章