Handler原始碼解析
Handler物件通過執行緒的MessageQueue,允許你傳送或者處理一個Message物件或者一個Runnable物件。
每一個Handler物件,都與一個單獨的執行緒和執行緒的MessageQueue相關聯。當你建立一個新的Handler物件,
這個Handler物件便與建立這個handler物件的執行緒和執行緒的MessageQueue相繫結。從這時起,這個handler物件
將傳入message物件或者runnable物件到MessageQueue中或者從MessageQueue中提取Message物件,並進行相應處理。Handler有兩個主要的作用:
(1)安排message或者runnable物件在未來的某個時間點執行;
(2)在其他的執行緒中執行相關邏輯。在應用程序被建立的時候,MainThread會建立一個MessageQueue,這個MessageQueue將會用於高等級的應用物件(例如:activitys、broadcast reveivers等)和任意一個它建立的windows。也可以建立子執行緒,與主執行緒進行交流。
1.主要程式碼分析
與Handler機制相關的有幾個類 Looper、MessageQueue、Message和ThreadLocal。
ThreadLocal
這個類提供執行緒本地變數,即為不同的執行緒變數提供不同的副本。通過get和set方法。ThreadLocal一般為類的私有靜態變數,用於關聯執行緒的state。
例如,下面這個類,用於為每一個執行緒建立唯一id。執行緒的id在第一次呼叫ThreadId.get()方法後被分配,在之後不會變化
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
每一個執行緒都持有這個執行緒的ThreadLocal的一個copy的明確引用。直到這個執行緒不在存活或者Threadlocal單例不存在。
1.ThreadLocalMap類
與ThreadLocal類相關的還有一個非常重要的類 ThreadLocalMap。真正的資料儲存是由ThreadLocalMap類完成的。在Thread類中包含一個ThreadLocalMap變數。不過這個變數是可見性是預設的,只能包內訪問到。
Thread.class
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap類是一個靜態內部類。自定義一個靜態類用於儲存資料的條目。
//繼承WeakReference 弱引用。已ThreadLocal物件為key,Object為value。組成鍵值對。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
- 變數
// 初始化的容量必須是2的倍數
private static final int INITIAL_CAPACITY = 16;
//儲存資料陣列,在必要是可以重新計算陣列長度。陣列長度必須是2的倍數。
private Entry[] table;
//儲存資料table容量。
private int size = 0;
//判斷是否需要重新計算陣列容量的標誌 預設值0
private int threshold; // Default to 0
- 構造方法
//建立一個新map,初始值為firstKey和firstValue。只有在至少一個entry放入的時候才會建立。
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];//建立儲存資料陣列,大小預設值為16。
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);//計算儲存位置。具體演算法沒看懂。
table[i] = new Entry(firstKey, firstValue);//存入資料
size = 1;//修改size
setThreshold(INITIAL_CAPACITY);//修改shreshold變數 為INITIAL_CAPACITY * 2 / 3;
}
//另外一個構造方法,引數為ThreadMap,即copy建立一個新的ThreadMap。私有方法
private ThreadLocalMap(ThreadLocalMap parentMap);
- get(hreadLocal
/**
*通過ThreadLocal key獲取value。因為儲存Entry是個弱引用和儲存資料位置雜湊可能有碰撞。
*所以會出現找不到的問題。則交與getEntryAfterMiss()方法來處理。
*/
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);//計算儲存位置
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);//未找到則交於getEntryAfterMiss()方法處理。
}
//處理無法直接從key獲取value的情況。
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e; //正常返回
if (k == null)
expungeStaleEntry(i);//從Entry獲取的key,key已經不存在了。則抹去對這個entry的儲存。
else
i = nextIndex(i, len);//key存在,但是已經不是之前值了。則重新計算位置並存儲entry。
e = tab[i];
}
return null;
}
- set(ThreadLocal
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//遍歷陣列,判斷陣列中是否已經存過相同key資料。
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) { // key與陣列中entry的key相等。則替換value。
e.value = value;
return;
}
if (k == null) { //陣列entry key為null。則替換這個entry
replaceStaleEntry(key, value, i);
return;
}
}
//陣列中不存在這個key 也不存在key為null的情況。則新建entry,並加入陣列。
tab[i] = new Entry(key, value);
int sz = ++size;
//cleanSomeSlots()方法查詢整個陣列,發現entry為null或者entry.get()為null。則
//刪除掉這個entry。如果有刪除過entry則返回true。否則返回false。
//假如沒有刪除過entry 並且增加後的陣列長度 >=threshold變數。則重新計算陣列大小。
//上面也說過 threshold 變數為陣列長度的三分之二。
//rehash將會將陣列容量擴大一倍。
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
2.主要方法
- initialValue()
//主要用於建立每個執行緒的ThreadLocal值的初始化。上面執行緒Id的例子中重寫了這個方法。
//已實現建立ThreadLocal便初始化Id的功能。這個方法會在Thread第一次呼叫ThreadLocal變數的get()方法時呼叫。
protected T initialValue() {
return null;
}
2.get()
public T get() {
//獲得當前執行緒
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//獲取thread對應的ThreadLocalMap成員變數。
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //通過key獲取value。
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();//如果未找到value則 初始
}
//初始化執行緒ThreadLocalMap變數
private T setInitialValue() {
T value = initialValue(); //獲取初始化值,上面的例子中用到這個方法。用於初始化執行緒Id。
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); //執行緒已存在ThreadLocalMap變數 則加入新初始化的entry。
else
createMap(t, value); //執行緒未初始化ThreadLocalMap變數則,初始化並加入新entry
return value;
}
3.set(T value)
//執行緒已存在ThreadLocalMap變數,則加入entry。
//執行緒未初始化ThreadLocalMap變數則,初始化並加入entry。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
4.如何計算儲存位置。即如何計算entry在ThreadLocalMap中陣列table的下標。
計算公式為:有ThreadLocal的nextHashCode()方法,即從0開始每次呼叫增加 HASH_INCREMENT = 0x61c88647
。即0x61c88647的倍數。
//firstKey.threadLocalHashCode = nextHashCode();
//INITIAL_CAPACITY為陣列長度。 預設為16.必須為2的倍數。
//因為INITIAL_CAPACITY為2的倍數。所以公式的表示為:取firstKey.threadLocalHashCode的低幾位。
//搜了下資料 這個數的選取與斐波那契雜湊有關。使用這種方式取的值分佈很均勻。XD.原因不懂。
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
MessageQueue
*用於Looper使用的快取message列表的初級類,並不是直接通過MessageQueue新增,而是通過與Handler相關聯的
Looper。你可以在當前執行緒中使用Looper.myQueue()方法獲取這個物件。*
主要方法
1.next()
// 取出message的方法。
Message next() {
、、、
for (;;) {
、、、
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {//msg.target即為發出這個message的Handler
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//當前時間戳小於msg.when,即下一個message還沒有準備好,則設定超市啟動時間。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 下一個message已經到時間。則將message從連結串列中取出並返回這個message。
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
、、、
}
}
2.enqueueMessage(Message msg, long when)
// 插入一個message。
boolean enqueueMessage(Message msg, long when) {
、、、
synchronized (this) {
、、、
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//佇列中沒有message 或者插入message.when為0 或者小於當前對列頭部message時間戳
//則將插入message 插入到佇列頭部。
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//判斷message的when並插入到佇列中間。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
Looper
*用於Threand中的訊息迴圈。執行緒預設是沒有這個迴圈的。使用prepare()方法建立。呼叫loop()
方法,開啟迴圈。*
下面例子為建立與Handler互動的Thread。
//eg:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//在這處理傳入message
}
};
Looper.loop();
}
}
主要變數
//sThreadLocal.get()在呼叫prepare()方法之前,返回null。
//Looper類中存在靜態的TheadLocal,並且擁有物件中擁有MessageQueue物件。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;
final MessageQueue mQueue;
final Thread mThread;
主要方法
1.loop()
//迴圈縣城中的MessageQueue。務必在退出的時候呼叫quit方法。
//把看不懂的程式碼刪掉之後,顯而易見,及從MessageQueue中取message。並呼叫
//message.target即傳送這個message的handler物件的dispatchMessage()方法。
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(); // might block
if (msg == null) {
return;
}
、、、
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
、、、
msg.recycleUnchecked();
}
}
2.prepare(boolean quitAllowed)
//新建一個Looper物件存入到這個當前執行緒的ThreadLocalMap變數中。
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));
}
Handler
前面的ThreadLocal、MessageQueue、Lopper已經清楚後,Handler的程式碼就很清晰了。
主要方法
1.dispatchMessage(Message msg)
//這個方法在Looper的loop()方法中呼叫。
//可以從這個方法中 看出Handler的回撥邏輯和順序。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
2.enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
//Handler 所有的傳送訊息的方法,最後都呼叫這個方法。
//該方法只是呼叫了Looper物件中的MessageQueue物件的enqueueMessage();方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
總結
Handler主要是使用ThreadLocal的在不同執行緒中儲存不同的副本的機制來實現。
在建立Handler物件的時候呼叫Looper.prepare()方法。為當前執行緒新增一個ThreadLocal物件用於
儲存一個Looper物件。而Looper物件中擁有一個MessageQueue物件。這樣就形成了每一個執行緒物件中都儲存一個
Looper物件和MessageQueue物件的結果。因此當Handler傳送資訊時,即向當前執行緒MessageQueue中加入一個message。
而當前執行緒的Looper則從MessageQueue中提取message。並執行Handler傳送message時設定的回撥。因為Looper在各個
執行緒中存在不同的副本。所以實現了跨執行緒通訊。