標籤:looper messagequeue handler android訊息處理機制
1. Thread Local Storage (線程局部儲存) 我們通過位於android.os包下的Looper.class源碼可以看到成員變數區有一個線程局部變數
sThreadLocal,該類的作用是線程局部儲存?那麼是線程局部儲存TLS?這個問題可以從變數範圍的角度來理解。 變數的常見範圍一般包括以下幾種。
- 函數內部變數。其作用地區是該函數,即每次調用該函數,該變數都會重新回到初始值。
- 類內部的變數。其作用就是該類所產生的對象,即只要該對象沒有被銷毀,則對象內部的變數則一直保持。
- 類內部的靜態變數。其作用是整個進程,即只要在該進程中,該變數的值就會一直保持,無論使用多少類來構造這個對象,該變數只有一個賦值,且一直保持。
對於類內部的靜態變數而言,無論是從進程中那個線程引用該變數,其值總是相同的,因為編譯器內部為靜態變數分配了單獨的記憶體空間。但有時我們卻希望,當從同一個線程中引用該變數時,其值總是相同的, 而從不同的線程引用該變數,其值應該不同,即我們需要一種範圍為線程的變數定義,這就是線程局部儲存。
ThreadLocal內部其實是使用弱引用WeakReference來儲存當前線程對象,但是不同的線程有不同的ThreadLocal對象,因此會有一個用於區別的id,由此內部封裝了一個靜態內部類Values,這個類就相當於一個map,儲存了每一個線程的執行個體的值。
sThreadLocal在prepare()函數被調用的時候就進行了一次賦值,即構造一個新的LooperObject Storage Service到本地線程局部變數中,當然一個線程只能有一個Looper對象,否則會拋出如所示的異常。
Looper中有2個用於構造Looper對象的方法。一個是prepare(),另一個是prepareMainLooper()。prepare()我們已經說過了,下面說下prepareMainLooper()。prepareMainLooper(),由方法注釋我們可以知道,這個應用程式的mainlooper對象是由android環境建立的,所以你自己永遠沒必要調用這個函數。
2. Looper我們可以從類注釋來瞭解Looper類的作用到底是幹什麼的。
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare in the thread that is to run the loop, and then loop to have it process messages until the loop is stopped. Most interaction with a message loop is through the Handler class. This is a typical example of the implementation of a Looper thread, using the separation of prepare and loop to create an initial Handler to communicate with the 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類的作用就是給線上程內部建立一個訊息迴圈,當然線程自身內部是沒有一個訊息迴圈機制;在run()函數首行調用Looper.prepare(),即使建立了一個訊息迴圈隊列,然後在run函數尾行調用Looper.loop()則開始處理訊息直到訊息迴圈停止。大多數訊息迴圈的互動是通過Handler類進行的。
3. Handler這裡我們只從訊息迴圈機制角度來分析這個Handler。首先看Handler的建構函式
/** * Default constructor associates this handler with the queue for the * current thread. * * If there isn‘t one, this handler won‘t be able to receive messages. */ public Handler() { 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()); } } //從TLS(局部線程儲存)中取出已經存放好的Looper對象 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can‘t create handler inside thread that has not called Looper.prepare()"); } //將Looper對象中的MessageQueue賦值給Handler中的對象 mQueue = mLooper.mQueue; mCallback = null; } |
然後我們在從使用的角度分析,這裡我只從非同步處理UI介面分析。我們(程式員)通過 Handler調用sendMessage()函數線上程內部向UI主線程發送一個Message對象,該Message對象會依次通過Handler中的函數
sendMessage(...) -> sendMessageDelayed(...) -> sendMessageAtTime(...) 最終會通過sendMessageAtTime發送訊息對象。 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; //將訊息對象加入到訊息佇列 sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }
然後我們在來看看enqueueMessage進行了什麼操作。
final boolean enqueueMessage(Message msg, long when) { ... if (needWake) { nativeWake(mPtr); } ... } nativeWake是一個java本地方法,這裡涉及了訊息機制中的Sleep-Wakeup機制,關於如何喚醒Looper線程的動作,這裡不做贅述,其最終就是調用了 native層的Looper的wake函數,喚醒了這個函數之後,就開始進行訊息迴圈 |
| |
Android中非同步訊息處理機制