“說一下handler機制”,這句話面試的時候被問過無數(shù)次。但是到底要怎么說,網(wǎng)上卻沒有一個標準答案,很多人洋洋灑灑一大篇加各種圖,實在讓人望而生畏,你是讓我面試的時候背這么一大篇嗎?
其實真的沒有那么復(fù)雜,干貨上前,標準答案:
Handler機制主要由Handler,Looper,MeesageQueue組成。
Looper在程序啟動時即創(chuàng)建并不斷的在循環(huán),直到Handler有消息發(fā)送到MessageQueue時,
Looper取出消息且回調(diào)給Handler來處理。
怎么樣,洋洋灑灑一大篇看不懂,這么幾句話,是不是簡單多了?
什么,你說不夠深入?
好吧,那我們來深入一下,《How to read a book》講,一定要提出問題,那我們就來深入的提出幾個問題:
1.Looper是個什么玩意,它是怎么創(chuàng)建的?它是什么時候開始循環(huán)的?
2.Handler是怎么把發(fā)送消息讓Looper知道的?
3.Looper又是怎樣取出消息且回調(diào)給Handler來處理的?
在這里,我們必須閱讀一下源碼了,我們來看這三個主要組成的其一Looper類,打開代碼,是不是很長一大片不知道怎么入手?哎呀好困難怎么辦。答案是,一點也不困難,因為我們閱讀源碼,并不是一行一行讀,而是只要看核心方法就夠了,什么是核心方法呢?Looper有倆個,prepare(),這個單詞的意思是準備;還有一個loop(),這個單詞的意思是循環(huán)。
我們來看perpare()
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
final Thread mThread;
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
是的沒有錯,就這些了,我們可以看到它做了三件事情:
第一、“sThreadLocal.set(new Looper(quitAllowed));” 把new出來的Looper給塞到ThreadLocal里面,我們不難理解ThreadLocal就是一個類似HashMap的東東;
第二、“ mQueue = new MessageQueue(quitAllowed);” new了一個MessageQueue;
第三、“mThread = Thread.currentThread();” 獲取當前線程的引用。
接下來是loop()
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) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
} finally {
}
msg.recycleUnchecked();
}
}
把不重要的代碼刪掉,是不是一目了然了,已經(jīng)不用多解釋了吧?
我們再來看一個類,ActivityTread的main[]方法,這個地方是程序啟動時就會調(diào)用的
public static void main(String[] args) {
Looper.prepareMainLooper();
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
class Looper{
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
}
一目了然,也就是說,程序啟動時,先prepare() new出來一個Looper,并且把它塞到ThreadLocal里面,以后在同一個線程要用的時候都從里面拿,避免創(chuàng)建多個Looper,然后就開始了loop(),死循環(huán)之路,也就是說程序無時無刻不在loop(),然后msg=null的時候,return,直到發(fā)現(xiàn)有了msg,msg.target.dispatchMessage(msg);交給msg的target,也就是handler來處理。
我們再來看下Handler的源碼,我們先看Handler的構(gòu)造函數(shù)
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;
}
Looper.myLooper,正是我們剛才說的,從ThreadLocal里面拿出本線程已有的Looper,然后MessageQueue,也是原來Looper里面的那個MessageQueue,接下來是Handler發(fā)送消息的方法,sendMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
return queue.enqueueMessage(msg, uptimeMillis);
}
不難理解,Handler并沒有真正意義的去“發(fā)送”,而是把消息放到了MessageQueue中而已。
最后,我們來看下Handler如何處理的消息,dispatchMessage
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
handleMessage(msg);
}
還記不記得,Loop.loop()中,一旦發(fā)現(xiàn)MessageQueue有值,就會調(diào)用msg.target.dispatchMessage(msg);走的就是這里啦,還記不記得我們平時寫Handler的時候,一般都是這樣
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
}
};
重寫的就是上面這個方法啦,至此,我們來回顧一下上面三個問題:
1.Looper是個什么玩意,它是怎么創(chuàng)建的?它是什么時候開始循環(huán)的?
2.Handler是怎么發(fā)送消息讓Looper知道的?
3.Looper又是怎樣取出消息且回調(diào)給Handler來處理的?
如果對照源碼仔細看了本文,相信這幾個問題已經(jīng)不難啦,而且你也學(xué)會了如何真正的閱讀源碼,那就是抓住主線而忽略掉不重要的部分,如果對你有幫助,點個喜歡吧!