Android中異步消息處理機制

1. Thread Local Storage (線程局部存儲)
我們通過位于android.os包下的Looper.class源碼可以看到成員變量區有一個線程局部變量sThreadLocal,該類的作用是線程局部存儲?那么是線程局部存儲TLS?這個問題可以從變量作用域的角度來理解。
變量的常見作用域一般包括以下幾種。
- 函數內部變量。其作用區域是該函數,即每次調用該函數,該變量都會重新回到初始值。
- 類內部的變量。其作用就是該類所產生的對象,即只要該對象沒有被銷毀,則對象內部的變量則一直保持。
- 類內部的靜態變量。其作用是整個進程,即只要在該進程中,該變量的值就會一直保持,無論使用多少類來構造這個對象,該變量只有一個賦值,且一直保持。 </ul> 對于類內部的靜態變量而言,無論是從進程中那個線程引用該變量,其值總是相同的,因為編譯器內部為靜態變量分配了單獨的內存空間。但有時我們卻希望,當從同一個線程中引用該變量時,其值總是相同的, 而從不同的線程引用該變量,其值應該不同,即我們需要一種作用域為線程的變量定義,這就是線程局部存儲。 </div>
ThreadLocal內部其實是使用弱引用WeakReference來存儲當前線程對象,但是不同的線程有不同的ThreadLocal對象,因此會有一個用于區別的id,由此內部封裝了一個靜態內部類Values,這個類就相當于一個map,存儲了每一個線程的實例的值。
sThreadLocal在prepare()函數被調用的時候就進行了一次賦值,即構造一個新的Looper對象存儲到本地線程局部變量中,當然一個線程只能有一個Looper對象,否則會拋出如下圖所示的異常。

prepare()我們已經說過了,下面說下prepareMainLooper()。

prepareMainLooper(),由方法注釋我們可以知道,這個應用程序的mainlooper對象是由android環境創建的,所以你自己永遠沒必要調用這個函數。


2. Looper
我們可以從類注釋來了解Looper類的作用到底是干什么的。 </div>
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函數,喚醒了這個函數之后,就開始進行消息循環 |
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!