一拧揽、Handler機制概述
Handler機制也可以說是消息機制慰照,Handler的運行時需要MessageQueue和Lopper的支持的灶挟。MessageQueue指消息隊列,用來存儲Message毒租,雖然名為隊列但其實是單向鏈表結(jié)構(gòu)稚铣;Lopper用于消息循環(huán),采用死循環(huán)的方式墅垮,如果有需要處理的消息就會處理惕医,否則就會阻塞等待。
Handler的工作原理如下:
Handler發(fā)送一個消息算色,調(diào)用MessageQueue的enqueueMessage方法將Message加入消息隊列抬伺;Lopper通過loop方法取出消息,最后轉(zhuǎn)發(fā)給Handler灾梦,最終在handlerMessage中處理峡钓。
二、源碼分析
2.1Handler構(gòu)造方法
Handler的構(gòu)造方法斥废,有很多椒楣,但是最后都是調(diào)用兩個方法,分別是Handler(Callback callback, boolean async)和Handler(Looper looper, Callback callback, boolean async)
public Handler(Callback callback, boolean async) {
//當設(shè)置為true時檢測匿名牡肉,本地或者成員類繼承Handler并且不是靜態(tài),這種類型的類會造成潛在的內(nèi)存泄漏
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());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " 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;
}
2.2 創(chuàng)建以及存儲Looper
我們可以從Handler的構(gòu)造方法中發(fā)現(xiàn)Looper是創(chuàng)建的必要參數(shù)之一淆九,那么創(chuàng)建Handler之前那就要先創(chuàng)建Looper统锤,那么Looper是如何來的,我們可以看到炭庙,一種是將Looper實例化通過構(gòu)造穿進來饲窿,另一種是Looper.myLooper()直接獲取的
- 自己創(chuàng)建一個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));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
可以看到創(chuàng)建Looper有兩種方式,一種是Looper.prepare()焕蹄,一種是Looper. prepareMainLooper()逾雄,第二種是給應用主線程創(chuàng)建Looper的,所以我們自己創(chuàng)建使用第一種腻脏。我們繼續(xù)看會發(fā)現(xiàn)Looper創(chuàng)建后會存放在ThreadLocal中鸦泳,我們來看一下這個ThreadLocal是如何存儲Looper的,看源碼:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
看到這個是不是很熟悉的東西:key永品、value做鹰,key是當前線程,value就是當前線程對應的Looper鼎姐,創(chuàng)建的時候還會通過ThreadLocal.get() 獲取當前線程的Looper判斷是否已經(jīng)創(chuàng)建過Looper钾麸,所以可以知道Looper是跟線程綁定的更振,一個線程只會有一個Looper不可重復創(chuàng)建,ThreadLocal是來存儲這些Looper的饭尝,key就是Looper所在的線程肯腕。
- Looper.myLooper()
我們再來看看Looper.myLooper()的源碼
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到是從ThreadLocal獲取存儲的Looper,再看ThreadLocal.get()源碼:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
就是返回當前線程所對應的Looper
所以我們可以知道钥平,在Handler創(chuàng)建前我們需要通過Looper.prepare()創(chuàng)建出當前線程自己的Looper乎芳,主線程則不需要創(chuàng)建,因為系統(tǒng)已經(jīng)創(chuàng)建了帖池,這個后面介紹奈惑。
2.3消息發(fā)送
那么Looper有了,Handler創(chuàng)建了睡汹,是如何發(fā)送消息的呢肴甸。我們可以發(fā)現(xiàn)發(fā)送消息有下面幾種方法:
- 1.sendMessage(Message msg)
- 2.sendEmptyMessage(int what)
- 3.sendEmptyMessageDelayed(int what, long delayMillis)
- 4.sendEmptyMessageAtTime(int what, long uptimeMillis)
- 5.sendMessageDelayed(Message msg, long delayMillis)
- 6.sendMessageAtTime(Message msg, long uptimeMillis)
- 7.sendMessageAtFrontOfQueue(Message msg)
這里1哆姻,2晦款,3伦籍,4撩笆,5最后調(diào)用的都是6颖榜。這里需要注意一個地方楞抡,就是系統(tǒng)是如何計算MessageDelayed的延遲時間的看铆,我們看一下源碼:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
可以看到是用SystemClock.uptimeMillis() + delayMillis法瑟,那么這個SystemClock.uptimeMillis()又是什么秽浇,源碼也有:
/**
* Returns milliseconds since boot, not counting time spent in deep sleep.
*
* @return milliseconds of non-sleep uptime since boot.
*/
@CriticalNative
native public static long uptimeMillis();
大概意思返回自啟動以來的毫秒數(shù)浮庐,不計算深度睡眠所花費的時間。注意了這里不是是用的手機當前時間柬焕。
2.4消息加入消息隊列
消息發(fā)送了审残,這些消息是怎么管理的,那就是通過消息隊列MessageQueue斑举,把這些消息加入到消息隊列中去搅轿。這個消息隊列雖然名為隊列卻是單向鏈表。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
接著看MessageQueue的enqueueMessage方法
boolean enqueueMessage(Message msg, long when) {
//handler為空拋異常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//消息已經(jīng)在隊列中拋異常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
//加入隊列
msg.markInUse();
//時間賦值
msg.when = when;
//當前隊列的第一個msg
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//當沒有頭消息或者加入消息的觸發(fā)事件為0或者早于頭消息的話就講加入消息放在隊頭
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
至此富玷,入隊列完全結(jié)束璧坟。從這里可以看出,延遲低的消息會被加到延遲高的消息之前赎懦。
2.5消息處理
消息進入消息隊列后就需要Lopper對消息進行提取雀鹃,使用Looper.loop()方法不斷的從消息隊列中取出消息。這個方法是需要手動調(diào)用的铲敛,activity除外褐澎,因為activity中系統(tǒng)已經(jīng)幫我們調(diào)用了。這個我們后面再看伐蒋,下面我們看看Looper.loop()的部分源碼:
public static void loop() {
......
for (;;) {
//拿到隊頭的消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
try {
//msg.target.dispatchMessage(msg)其實就是Handler.dispatchMessage(msg);
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
}
}
這里loop是一個無限循環(huán)的操作工三,queue.next()拿到消息然后發(fā)送給handler迁酸,那如果拿到是的是延遲消息怎么辦,我們來看看queue.next()源碼:
Message next() {
......
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
//拿到當前系統(tǒng)自啟動來的未休眠時間
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 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) {
//當前系統(tǒng)自啟動來的未休眠時間小于消息出發(fā)時間是不會返回msg的
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
//到了消息觸發(fā)事件俭正,返回msg
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
也就是只有到了消息觸發(fā)的時間調(diào)用next才會返回msg奸鬓。至此全部結(jié)束。這里面還有一個知識點掸读,就是IdleHandler串远,這個我們等下講。
三儿惫、現(xiàn)在來講講上面遺留的問題
- 為什么Activity中使用Handler不需要我們創(chuàng)建Looper
- 為什么Activity中使用Handler不需要我們自己調(diào)用Looper.loop()去循環(huán)獲取消息
- Looper.loop()是個無限循環(huán)澡罚,且會阻塞線程,那么主線程為什么看起來沒有阻塞呢
我們來看一下源碼:
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到在ActivityThread中Looper.prepareMainLooper()創(chuàng)建的就是主線程中的Looper肾请,這里是系統(tǒng)自己創(chuàng)建好的留搔;隨后創(chuàng)建了MainThreadHandler,主線程中自己的Handler铛铁;再然后調(diào)用了Looper.loop()隔显。這里一、二兩個問題就知道答案了饵逐,第三個問題的關(guān)鍵就在這個MainThreadHandler括眠,系統(tǒng)就是使用這個MainThreadHandler去處理接收到的消息的,所以主線程看起來就沒有被阻塞倍权。我們來看一下MainThreadHandler是通過thread.getHandler()方法獲取的掷豺,我們看一下這個方法:
final Handler getHandler() {
return mH;
}
我們再看一下mH怎么來的:
final H mH = new H();
繼續(xù):
class H extends Handler {
......
public static final int GC_WHEN_IDLE = 120;
public static final int RELAUNCH_ACTIVITY = 160;
......
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
case BIND_APPLICATION: return "BIND_APPLICATION";
......
}
}
return Integer.toString(code);
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
......
case GC_WHEN_IDLE:
scheduleGcIdler();
break;
......
case RELAUNCH_ACTIVITY:
handleRelaunchActivityLocally((IBinder) msg.obj);
break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
}
主線程會使用這個mH來處理消息。
我們在這里也可以看到之前在Loop.loop()方法中看到的IdleHandler账锹,這里舉例用scheduleGcIdler()方法
四萌业、IdleHandler是什么
我們來跟一下scheduleGcIdler()源碼:
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
我們可以看到Looper.myQueue().addIdleHandler加入了一個mGcIdler,我們來看一下這個mGcIdler:
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
return false;
}
}
我們發(fā)現(xiàn)IdleHandler原來是一個接口:
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
這個queueIdle()方法表示在消息隊列中消息用完后立即調(diào)用奸柬,如果返回true則會保持在系統(tǒng)空閑時不斷調(diào)用,false則只調(diào)用一次就被刪除婴程。如果消息隊列中有消息廓奕,就會被掛起等待消息處理。我們現(xiàn)在在來看看MessageQueue.next()方法:
Message next() {
....
for (;;) {
......
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
......
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
......
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
......
}
}
可以在這里看到IdleHandler執(zhí)行過程確實如此档叔。
五桌粉、補充知識點
5.1HandlerThread是什么
我們通過觀察源碼可以發(fā)現(xiàn)HandlerThread繼承自Thread所以他擁有Thread能力,與之不同的是HandlerThread內(nèi)部自建創(chuàng)建了一個Handler衙四,提供了Looper铃肯。
5.2Message.obtain()
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
從消息池中返回新的消息,且不會分配新對象传蹈,避免了消息對象的重復創(chuàng)建跟銷毀押逼。
5.3怎么維護消息池
消息池也是鏈表結(jié)構(gòu)步藕,在消息被消費后消息池會執(zhí)行回收操作,將該消息內(nèi)部數(shù)據(jù)清空然后添加消息鏈表最前面挑格。