Handler簡單介紹與使用
說起Handler堪簿,大多數(shù)Android開發(fā)者會想到:在子線程中更新UI,這確實(shí)是Handler的主要用途之一。分析Handler的運(yùn)行機(jī)制托猩,就從最簡單的使用開始吧:
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.i("thread",Thread.currentThread().getName());
Bundle data = msg.getData();
Log.i("message",data.getString("msg"));
return true;
}
});
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("msg","This is in the child thread message");
message.setData(bundle);
mHandler.sendMessage(message);
}
}).start();
}
上面的代碼應(yīng)該沒什么好解釋的碧磅,首先在onCreate方法中第三行創(chuàng)建了Handler對象碘箍,然后在子線程中創(chuàng)建出一個Message對象,并往Message對象中set了一個Bundle鲸郊,最終調(diào)用Handler的sendMessage方法丰榴。Handler的handleMessage方法會被調(diào)用,這個handleMessage方法是回調(diào)在主線程的秆撮,我們也就成功實(shí)現(xiàn)了將子線程的消息發(fā)送給主線程處理四濒,因此控制臺輸出如下:
I/thread: main
I/message: This is in the child thread message
對于使用而言,知道這么用就行了,但本文是對Handler運(yùn)行機(jī)制的總結(jié)盗蟆,因此會繼續(xù)深入戈二。
先來看Handler的構(gòu)造器:
public Handler(Callback callback, boolean async) {
...
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;
}
從這里我們至少能得到以下信息:Handler與Looper,MessageQueue有關(guān)喳资。在構(gòu)造方法中第一行就是獲取Looper觉吭,跟進(jìn)到myLooper方法中:return sThreadLocal.get();
實(shí)際上是獲取當(dāng)前線程的Looper,最后將當(dāng)前線程的Looper對象引用賦給了自己的成員變量mLooper仆邓,如果獲取到的Looper是null鲜滩,就會拋出異常。接著看mLooper.mQueue
节值,這個mQueue實(shí)際上就是當(dāng)前線程的Looper中的MessageQueue徙硅,在Handler的構(gòu)造器中,將當(dāng)前線程Looper中的MessageQueue對象引用賦給了自己的成員變量mQueue搞疗。
為何在子線程中創(chuàng)建Handler會報異常嗓蘑?
還記得剛才Handler的構(gòu)造器中需要獲取當(dāng)前線程的Looper嗎?如果獲取不到贴汪,就會拋出異常脐往,我們實(shí)驗(yàn)一下:
new Thread(new Runnable() {
@Override
public void run() {
mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
}
}).start();
果然GG:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
這是因?yàn)樽泳€程中,默認(rèn)是沒有Looper的扳埂,所以當(dāng)然會報錯业簿。那按照它提供的方法,在創(chuàng)建Handler前阳懂,調(diào)用一下Looper.prepare方法梅尤,就不會有問題了。那這個prepare方法究竟做了什么?實(shí)際上猜都能猜出來岩调,肯定是給當(dāng)前線程創(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));
}
我們可以看到sThreadLocal.set(new Looper(quitAllowed));
這里就往當(dāng)前線程中設(shè)置了Looper巷燥。子線程有了Looper,Looper中有MessageQueue号枕,滿足了創(chuàng)建Handler的條件缰揪。雖然這樣寫,創(chuàng)建Handler是沒有問題了葱淳,但是此時Handler是無法處理Message的钝腺,還必須要調(diào)用下Looper.loop方法,讓Looper開始循環(huán)取出MessageQueue中的消息交給Handler赞厕。那為何主線程不需要調(diào)用prepare方法就能直接創(chuàng)建Handler了呢艳狐?因?yàn)樵贏ctivityThread中,已經(jīng)幫我們創(chuàng)建好了Looper皿桑,所以無需我們手動再去創(chuàng)建毫目。因此蔬啡,如果我們想在子線程中創(chuàng)建Handler,并正常使用镀虐,應(yīng)該這樣寫:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
Looper.loop();
}
}).start();
Handler的工作流程
其實(shí)說了這么多箱蟆,依然不知道Handler、Looper粉私、MessageQueue三者間到底是什么關(guān)系顽腾,是如何協(xié)作工作的近零。在這里诺核,就先簡單說一下它們?nèi)唛g的關(guān)系,后面再具體分析每一個久信。當(dāng)創(chuàng)建了Handler以后窖杀,Handler就獲取到了當(dāng)前線程的Looper和MessageQueue。MessageQueue顧名思義就是消息隊(duì)列的意思裙士,維護(hù)著當(dāng)前線程未處理的消息列表入客。雖然叫隊(duì)列,但是它內(nèi)部的數(shù)據(jù)結(jié)構(gòu)是單鏈表腿椎,這樣做的優(yōu)點(diǎn)就是增加和刪除速度更快桌硫,因?yàn)閷τ贛essageQueue來說,最主要的操作就是新增啃炸、讀取和刪除铆隘。當(dāng)調(diào)用Handler的send系列方法時,會向MessageQueue中放入這個Message南用,放入了Message后膀钠,怎么才能交給Handler處理呢?答案是Looper裹虫。Looper的作用簡單理解就是一個死循環(huán)肿嘲,不斷的從MessageQueue中取出Message交給Handler,Handler就會根據(jù)dispatchMessage的分發(fā)對消息進(jìn)行處理筑公。注意Looper所在的線程是創(chuàng)建Looper的線程雳窟,我們在主線程中創(chuàng)建Handler使用的是主線程的Looper,因此handleMessage的調(diào)用是在主線程匣屡,而如果我們在子線程通過Looper.prepare創(chuàng)建了Looper封救,這個Looper是屬于子線程的,自然Looper中的MessageQueue也是在子線程中的耸采。這個時候我們在子線程創(chuàng)建Handler兴泥,這個Handler獲取到的Looper就是這個子線程的Looper,那么Looper循環(huán)MessageQueue時取出的Message在交給handleMessage處理時虾宇,也是處于子線程的搓彻。不要以為只要用了Handler,它的handleMessage就一定是回調(diào)在主線程,這個是和Handler所使用的Looper有關(guān)的旭贬。
MessageQueue
前面提到過MessageQueue是Android中的消息隊(duì)列怔接,雖然叫隊(duì)列,但是內(nèi)部實(shí)現(xiàn)并不是隊(duì)列稀轨,而是單鏈表扼脐。當(dāng)我們調(diào)用Handler的sendMessage方法的時候,其實(shí)最終都是向MessageQueue中插入了一個Message:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最終奋刽,調(diào)用的是enqueueMessage方法瓦侮,這個方法里面,就會去調(diào)用當(dāng)前線程的Looper中的MessageQueue的enqueueMessage方法佣谐,這個方法就是往MessageQueue中插入一個Message肚吏。光插入了消息,如何才能取出來交給Handler處理呢狭魂?這就是Looper的事情了罚攀。
Looper
Looper是與線程所關(guān)聯(lián)的,一個線程只能有一個Looper雌澄,主線程初始化時斋泄,默認(rèn)就會創(chuàng)建Looper,這也是為何能直接在主線程中創(chuàng)建使用Handler的原因镐牺。主線程創(chuàng)建Looper是在ActivityThread.java中的main方法中創(chuàng)建的炫掐,這也是Android應(yīng)用程序的入口:
//省略部分代碼
public static void main(String[] args) {
//創(chuàng)建Looper
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"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//開始消息循環(huán)
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我們知道,要讓Looper開始消息循環(huán)任柜,需要調(diào)用Looper.loop方法卒废,這也是為什么我們在子線程中創(chuàng)建Handler后,還要調(diào)用Looper.loop的原因宙地,如果不調(diào)用摔认,loop方法是不執(zhí)行的,也就不會有循環(huán)讀取消息隊(duì)列中的消息交給Handler處理了宅粥。重點(diǎn)看下Looper的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
Message msg = queue.next();
if (msg == null) {
return;
}
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
首先獲取了當(dāng)前線程的Looper和MessageQueue参袱,然后開起了死循環(huán),不停的從MessageQueue中取出Message(通過MessageQueue的next方法)秽梅。如果取出的Message不為null抹蚀,就會執(zhí)行msg.target.dispatchMessage(msg);
target是什么呢?我們再回過頭看看Handler將Message放進(jìn)MessageQueue的代碼:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
target就是你正在使用的那個Handler的對象企垦。因此當(dāng)Looper從MessageQueue中取出一條Message后环壤,就會調(diào)用對應(yīng)的Handler對象中的dispatchMessage方法。如果MessageQueue中已經(jīng)沒有任何消息了钞诡,就會一直阻塞在MessageQueue的next方法郑现,直到有新消息到來湃崩。
Handler
既然Looper從MessageQueue中取出Message后,調(diào)用了Handler的dispatchMessage方法接箫,我們就來看下dispatchMessage方法中攒读,做了什么事情?
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
感覺有點(diǎn)疑問辛友,msg.callback薄扁,這個callback是什么?查看Message類的成員變量發(fā)現(xiàn):Runnable callback
废累,什么時候用到呢邓梅?我們回想一下Handler中有post這個方法,用法很簡單九默,例如我們要在子線程中更新UI震放,也可以這樣做:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
Log.i("thread", Thread.currentThread().getName());
}
});
}
}).start();
}
這個Runnable中的run方法就是回調(diào)在主線程的,其實(shí)原理都是一模一樣的驼修,我們看Handler的post方法
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
原來,和我們調(diào)用send系列方法一樣诈铛,最終都是將Message放進(jìn)MessageQueue中乙各!只不過send系列方法是你顯示傳入一個Message,而post是你傳入一個Runnable幢竹,它內(nèi)部調(diào)用getPostMessage方法給你隱式構(gòu)建一個Message耳峦,并且把構(gòu)建的Message對象的callbak字段賦值為傳過來的Runnable。因此回到Handler的dispatchMessage方法焕毫,如果我們使用post方式蹲坷,則msg.callback就不為null,就會執(zhí)行handleCallback:
private static void handleCallback(Message message) {
message.callback.run();
}
這樣就調(diào)用了Runnable中的run方法邑飒。而如果我們使用的send方式循签,那么msg.callback就是null,就會執(zhí)行下面的:else語句塊:
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
這里的mCallback還有印象嗎疙咸?回想下我們之前看的Handler的構(gòu)造器中有這么一句:
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
...
mCallback = callback;
...
}
這個mCallback就是我們之前在創(chuàng)建Handler時傳的匿名內(nèi)部類县匠,因此我們匿名內(nèi)部類中的handleMessage方法就會被回調(diào)。注意看最后一行撒轮,還有一個handleMessage乞旦,這個在什么情況下會用呢?比如你這樣創(chuàng)建Handler時:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
這樣創(chuàng)建Handler题山,mCallback就為null兰粉,因此回調(diào)的就是Handler的handleMessage了。
通過查看dispatchMessage源碼,我們現(xiàn)在可以說:優(yōu)先級最高的是handleCallback笑诅,其次是Callback中的handleMessage,這個回調(diào)中的返回值決定了是否還要繼續(xù)回調(diào)Handler中的handleMessage衷笋,優(yōu)先級最低的就是Handler中的handleMessage客峭。
總結(jié)
要使用Handler豫领,就必須要有Looper,有了Looper舔琅,就自然有了MessageQueue等恐。主線程中,自帶了Looper备蚓,因此不需要手動創(chuàng)建Looper就可以正常使用Handler课蔬。而子線程中,默認(rèn)是沒有Looper的郊尝,因此要想在子線程中使用Handler二跋,就必須要手動為當(dāng)前線程創(chuàng)建Looper。創(chuàng)建的方法是Looper.prepare流昏,這樣當(dāng)前線程就擁有了Lopper和MessageQueue扎即,再創(chuàng)建Handler,就能與這個線程的Looper和MessageQueue關(guān)聯(lián)了况凉。但是要想實(shí)現(xiàn)消息循環(huán)谚鄙,還需要調(diào)用Looper.loop。
當(dāng)你使用Handler的send系列方法或是post系列方法時刁绒,本質(zhì)上都是將Message放入與當(dāng)前Handler所關(guān)聯(lián)的消息隊(duì)列中闷营,通過Looper循環(huán)取出Message再交給Handler處理。同樣的知市,Activity中的runOnUiThread也就沒什么好神奇的了傻盟,只要你搞懂了Handler的原理,一看代碼就懂了:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
好了嫂丙,到這里就總結(jié)完成了娘赴。如果文中對知識的理解有錯誤,歡迎指正奢入,共同學(xué)習(xí)進(jìn)步筝闹。謝謝!