Handler是什么?
Android中的異步消息處理機(jī)制,使用者可以在不阻塞UI線程的前提下輕松的實現(xiàn)消息管理和發(fā)送。
原理:
handler機(jī)制中包含4個關(guān)鍵類(下面對源碼的解析也是從這4個類入手),Message(消息),MessageQueue(消息隊列),Looper(輪詢器),Handler(消息發(fā)送和接收并處理),簡單一句話概括就是:handler負(fù)責(zé)發(fā)送message,將其加入到MessageQueue中,Looper不間斷的從MessageQueue中取出消息,并發(fā)送給對應(yīng)的handler實例去處理。
重點:源碼解析
有些同學(xué)不知道如何去看源碼,這就很尷尬了,往往想知道系統(tǒng)里面的源碼或者牛逼的開源項目是如何設(shè)計的,但是就是不知道如何入手,還有些同學(xué)覺得看源碼沒用,懂得怎么用不就行了嗎,非也,懂得如何用那只是招式,可能換個地方換種形式你就不認(rèn)識了,懂源碼,那是心法,知其所以然,才能千變?nèi)f化!
首先,我們使用的時候是這樣的:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//消息處理
·········
}
};
Message msg = Message.obtain();
handler.sendMessage(Message msg);
定義一個handler,并重寫了他的handleMessage()方法;我們來看看它的構(gòu)造方法:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
@hide
public Handler(boolean async) {
this(null, async);
}
@hide
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;
mCallback = callback;
mAsynchronous = async;
}
@hide
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
蒙蔽了,這么多!但是注意,后面三個你都是沒法使用的,因為有@hide;
先看空參構(gòu)造,他直接調(diào)用了Handler(Callback callback, boolean async);看看里面做了什么:
- 獲取looper對象:Looper.myLooper(); 進(jìn)去可以看到 返回了,sThreadLocal.get();這個是什么玩意?ThreadLocal,他是一個容器,里面封裝了一個map,以當(dāng)前線程的ThreadLocal作為key,以你要存的值作為value,我們這里value就是looper對象,這個容器是專門用來保存線程所特有的變量的,起到了線程間隔離的作用,如果有人想知道,我專門開一篇博客來講。至于looper是怎么初始化的,客官莫急,下面會講。
- mQueue = mLooper.mQueue;拿到消息隊列,這是一個鏈表結(jié)構(gòu),注:一個線程對應(yīng)一個looper,一個looper同樣也只有一個queue;
- callback這個是handler里面的一個接口,在消息分發(fā)的時候會講到,默認(rèn)是null,mAsynchronous這個參數(shù)表示消息是否是異步消息,默認(rèn)是false;
接下來看Message.obtain():
public static Message obtain() {
synchronized (sPoolSync) { //同步代碼塊
if (sPool != null) {
Message m = sPool; //sPool是一個靜態(tài)的Message 引用
sPool = m.next; //next也是Message,但是他不是靜態(tài)的;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
}
return new Message();
}
obtain()的實現(xiàn)非常有意思,我們知道m(xù)essage其實是復(fù)用的,message中有一個方法:recycle(),大家可以去看一下,消息被處理完畢后會調(diào)用recycle()方法,將message還原,并將sPool這個賦值為this,也就是當(dāng)前自己的實例對象,如果sPool是null那么當(dāng)前沒有消息可以復(fù)用,直接new出來并返回,如果不是null,那么將當(dāng)前的sPool返回,那么這個next又是干什么的呢?代碼可見,sPool =m.next;將sPool重新賦值,這個m.next就是一條將要處理的消息,也就是說每一個msg里面都有對下一個將要處理的消息的引用,這樣,sPool被賦值了,下次再執(zhí)行obtain()的時候,sPool指向的其實是另外一個Message實例了,至于next如何賦值,稍后在MessageQueue中詳細(xì)解析。
Message拿到了,開始發(fā)消息吧,sendMessage(Message msg);
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0); //直接調(diào)用sendMessageDelayed方法
}
通過源碼,一層一層往下找,發(fā)現(xiàn):sendMessage ——> sendMessageDelayed ——>sendMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//此方法才是真正發(fā)消息
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) {
msg.target = this; //重點:this表示的是當(dāng)前的handler實例,每個message都會記錄發(fā)送它的那個handler
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); //這里調(diào)用了messageQueue的方法;
}
重點:為什么msg要保存handler的引用呢?
我們都知道,handler機(jī)制消息處理,哪一個handler實例發(fā)出的消息,那么那一個handler就負(fù)責(zé)處理這個消息,這里的msg.target = this; 作用就在這,用來記住誰將msg發(fā)出來,等到處理的時候誰就來處理。
接著分析,handler最后調(diào)用了queue.enqueueMessage(msg, uptimeMillis);
我們來看看這個方法:(比較多,我精簡了一下,留下重要的部分)
boolean enqueueMessage(Message msg, long when) {
msg.markInUse();
msg.when = when;
Message p = mMessages;//當(dāng)前將要被處理的msg
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
mMessages是即將要處理的message,先看第一個if最有一個條件,when<p.when,這個when是什么呢,它是一個時間毫秒值,when = 發(fā)送消息的那一刻的時間+delytime(延后的時間);表示這個msg將要在什么時刻可以被處理,when越小,越要被有先處理,所以,if判斷新來的msg是否優(yōu)先級比當(dāng)前即將要被處理的高,如果是,那么將此消息放在第一位(mMessages = msg),并且還做了另外一件事,msg.next = p; p下一個要處理的msg,else里面道理也是一樣的,for循環(huán)比較
重點又來了! Looper!
上面留下了一個問題,Looper是怎么初始化的呢,app中的通信,UI的刷新,都需要依賴handler,那么,我們就猜想,Looper在app啟動的時候就已經(jīng)開始創(chuàng)建并初始化了,那么我們?nèi)ピ创a中找 ActivityThread main()方法 ;
public static void main(String[] args) {
````````
Looper.prepareMainLooper(); //在這里!
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop(); //還有這里
throw new RuntimeException("Main thread loop unexpectedly exited");
}
果然,在主線程啟動的時候就已經(jīng)啟動了Looper并調(diào)用了,prepareMainLooper()和loop();接著去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();
}
}
private static void prepare(boolean quitAllowed) {
//判斷l(xiāng)ooper是否是null如果是,就創(chuàng)建,并將其存到ThreadLocal中,上面說的handler中的looper就是從ThreadLocal中取出來的;
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
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;
Binder.clearCallingIdentity(); //這里大家不用管,我個人理解是對進(jìn)程的校驗,有知道的同學(xué)也可以留言告訴我。
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg); //分發(fā)消息
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked(); //消息的回收
}
}
三個方法,prepareMainLooper()其實內(nèi)部調(diào)用了prepare(false);這個boolean參數(shù)是什么意思呢,意識就是不允許messageQueue退出,這個參數(shù)會在構(gòu)建Queue的時候傳遞進(jìn)去,因為主線程的消息隊列只有在應(yīng)用退出的時候才允許退出,否則······沒有否則,消息機(jī)制都沒了,還怎么玩!
prepare()方法創(chuàng)建了一個looper,但是looper并沒有啟動,啟動的方法是下面的loop();loop()有一個for(;;),死循環(huán),里面做的工作就是一直取消息,并處理,然后recycleUnchecked()復(fù)用。這個時候looper就啟動起來了,只要你的應(yīng)用還在運行,他就會一直在。looper拿到消息后會通過msg.target.dispatchMessage(msg); 將消息發(fā)出來給handler處理,這里的msg.target不就是我們發(fā)消息的時候初始化的handler嗎?(篇幅有點長,不記得可以往上翻)
繼續(xù)看,又到重點了
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
通過一系列判斷,來處理消息,如下:
- 判斷msg.callback是不是null,如果不是,那么就給這個callback處理,這個callback是message中的一個Runnable;Message.obtain()其實是有其他參數(shù)的方法的,其中有一個是obtain(Handler h, Runnable callback);如果你用了這個,那么消息就會在你實現(xiàn)的Runnable中接收到處理的回調(diào);
- 第二個是判斷handler內(nèi)部的callback是不是null,如果不是null,就讓他去處理,這里的Callback可不是Runnable了,他是一個interface,里面定義了一個handleMessage(Message msg);方法,這個怎么實現(xiàn)呢?handler類里同樣有Handler(Callback callback)構(gòu)造方法
- 最后才輪到handler類里的方法handleMessage來處理消息。也就是我們在開始寫的那個簡單的handler使用方法。