Handler機(jī)制
Handler介紹:
我們都知道Android的主線程不能做耗時(shí)任務(wù),否者會(huì)導(dǎo)致ANR.但是界面的更新又必須在主線程中進(jìn)行,這樣我們就必須在子線程中處理耗時(shí)的任務(wù),然后在主線程中更新UI.
但是,我們?cè)趺粗雷泳€程何時(shí)完成任務(wù),又應(yīng)該什么時(shí)候更新UI,更新什么內(nèi)容,為了解決這個(gè)任務(wù),Android為我們提供了一個(gè)消息機(jī)制即Handler機(jī)制.
源碼分析:
創(chuàng)建Handler時(shí)無參構(gòu)造函數(shù)內(nèi)調(diào)用了兩個(gè)參數(shù)的構(gòu)造函數(shù),而在兩個(gè)參數(shù)的構(gòu)造函數(shù)中就是將一些變量進(jìn)行賦值.
通過Looper中的myLooper方法來獲得Looper實(shí)例的,如果Looper為null的話就會(huì)拋無法在未調(diào)用Looper.prepare()的線程內(nèi)創(chuàng)建handler異常
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;
在調(diào)用Looper.myLooper()之前必須先調(diào)用Looper.prepare()方法
public static void prepare() {
prepare(true);//prepare()方法調(diào)用了prepare(boolean quitAllowed)方法
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//如果Looper為空,實(shí)例化了一個(gè)Looper,然后將Looper設(shè)置進(jìn)sThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
從上面代碼中可以看到,prepare()方法調(diào)用了prepare(boolean quitAllowed)方法,實(shí)例化了一個(gè)Looper,然后將Looper設(shè)置進(jìn)sThreadLocal中,由此可見Looper是唯一的,多次調(diào)用Looper.prepare()會(huì)報(bào)異常
什么是ThreadLocal:
Threadlocal是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類,通過它可以在指定的線程中存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)以后,只有在指定線程中可以獲取到存儲(chǔ)的數(shù)據(jù),對(duì)于其他線程來說無法獲取到數(shù)據(jù).
雖然在不同線程中訪問的是同一個(gè)ThreadLocal對(duì)象,但是它們通過ThreadLocal獲取到的值卻是不一樣的.
一般來說,當(dāng)某些數(shù)據(jù)是以線程為作用域并且不同線程具有不同的數(shù)據(jù)副本的時(shí)候,就可以考慮采用ThreadLocal
知道了ThreadLocal.set()方法的作用,則Looper.prepare()就是將Looper與當(dāng)前線程進(jìn)行綁定(當(dāng)前線程就是調(diào)用Looper.prepare方法的線程)
Looper.prepare()方法在主線程中Android系統(tǒng)已經(jīng)幫我們?cè)贏ctivityThread類的main方法中調(diào)用Looper.prepareMainLooper();從當(dāng)前線程中的ThreadLocal中取出Looper實(shí)例
public static void prepareMainLooper() {
prepare(false);//這里調(diào)用了prepare方法
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();//從當(dāng)前線程中的ThreadLocal中取出Looper實(shí)例
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
通過上面的代碼,我們可以知道m(xù)Queue就是MessageQueue,在我們調(diào)用Looper.prepare方法時(shí)就將mQueue實(shí)例化了,拿到Looper中的mQueue這個(gè)成員變量,然后再賦值給Handler中的mQueue
mQueue = mLooper.mQueue;
Handler明明是在子線程發(fā)送的消息怎么會(huì)跑到主線程中?
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);
}
其實(shí)Handler.sendMessage()、sendEmptyMessage等方法最終都是調(diào)用sendMessageAtTime(),返回enqueueMessage()方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg.target = this; 就是將當(dāng)前的Handler對(duì)象賦值給了Message中的target變量,從而將調(diào)用sendMessage()方法的Handler與message進(jìn)行綁定.
queue.enqueueMessage(msg,uptimeMillis)就是調(diào)用了MessageQueue中的enqueueMessage(),將消息放入到消息隊(duì)列中,如果消息成功放入消息隊(duì)列,返回true,失敗返回false,而失敗的原因通常是因?yàn)樘幚硐㈥?duì)列正在退出.
Handler在sendMessage時(shí)會(huì)將自己設(shè)置給Message的target變量即將自己與發(fā)送的消息綁定。Handler的sendMessage是將Message放入MessageQueue中。
怎樣從MessageQueue中獲取Message
在ActivityThread類main()中,結(jié)尾處有Looper.loop();
public static void loop() {
final Looper me = myLooper();//通過myLooper方法拿到與主線程綁定的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//從Looper中得到MessageQueue
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//開始死循環(huán)
for (;;) {
//從消息隊(duì)列中不斷取出消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
//這句代碼是重點(diǎn)
msg.target.dispatchMessage(msg);
Looper.loop();就是拿到用與主線程綁定的Looper對(duì)象,從Looper對(duì)象中得到MessageQueue,死循環(huán)從queue.next()不斷取出消息,若隊(duì)列中無消息,則阻塞等待.
msg.target.dispatchMessage(msg);在msg.target就是發(fā)送消息的那個(gè)Handler,所以這句代碼本質(zhì)就是調(diào)用了Hadler的dispatchMessage()方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
//if中的代碼其實(shí)是和if (msg.callback != null) {handleCallback(msg);}
//原理差不多的,只不過mCallback是Handler中的成員變量。
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//當(dāng)上面的條件都不成立時(shí),就會(huì)調(diào)用這句代碼
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
msg.callback就是Runnable對(duì)象,當(dāng)msg.callback不為null時(shí)會(huì)調(diào)用 handleCallback(msg)方法
那什么情況下if (msg.callback != null)這個(gè)條件成立呢!就是調(diào)用Handler的post方法呀,最后在Runnable的run方法中來處理
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;
}
post方法最終也是將Runnable封裝成消息,然后將消息放進(jìn)MessageQueue中
public void handleMessage(Message msg) {
//它就是一個(gè)空方法,具體的代碼是我們實(shí)例化Handler重寫handleMessage()這個(gè)方法進(jìn)行處理的
}
總結(jié)
Hanler機(jī)制主要是四個(gè)類
- Handler :負(fù)責(zé)發(fā)送和處理消息
- Message:用來攜帶數(shù)據(jù)
- MessageQueue:消息隊(duì)列
- Looper:消息泵,負(fù)責(zé)不斷的從MessageQueue中取Message
Handler
- 在實(shí)例化Handler的時(shí)候要先調(diào)用Looper.prepare()
Looper.prepare()方法就是實(shí)例化了一個(gè)Looper,然后將Looper設(shè)置進(jìn)ThreadLocal中,將Looper與當(dāng)前線程綁定
但是,在主線程中Android系統(tǒng)已經(jīng)幫我們調(diào)用了Looper.prepare方法可以看下ActivityThread類中的main方法
Looper.prepareMainLooper();這句話的實(shí)質(zhì)就是調(diào)用了Looper的prepare方法
- 在實(shí)例化Handler的時(shí)候,通過Looper.myLooper()獲取Looper,然后在獲取Looper中的MessageQueue.
Looper.myLooper()就是從ThreadLocal中獲取Looper對(duì)象,Looper構(gòu)造中創(chuàng)建MessageQueue
- 在子線程中調(diào)用Handler的sendMessage方法時(shí)會(huì)將自己設(shè)置給Message的target變量即將自己與發(fā)送的消息綁定,將Message放入MessageQueue中,然后調(diào)用Looper.loop()方法來從MessageQueue中取出Message,執(zhí)行msg.target.dispatchMessage(msg)
Handler的無論時(shí)sendMessage、sendEmptyMessage等方法最后調(diào)用了sendMessageAtTime()方法,sendMessageAtTime()方法最后返回的是enqueueMessage()
enqueueMessage()方法最后是MessageQueue中的enqueueMessage方法,將消息放進(jìn)消息隊(duì)列中,如果消息已成功放入消息隊(duì)列,則返回true。失敗時(shí)返回false,而失敗的原因通常是因?yàn)樘幚硐㈥?duì)列正在退出。
- 通過Looper.loop()從MessageQueue中取出消息
ActivityThread類中的main方法結(jié)尾處,Looper.loop()方法,通過myLooper()方法拿到與主線程綁定的Looper,從Looper中得到MessageQueue,通過next()方法獲取Message,沒有消息時(shí)阻塞
msg.target.dispatchMessage(msg);說明已經(jīng)從消息隊(duì)列中拿到了消息,msg.target也就是發(fā)送消息的那個(gè)Handler,所以這句代碼的本質(zhì)就是調(diào)用了Handler中的dispatchMessage(msg)方法
dispatchMessage(msg)中判斷是post()還是send(),如果是post()就是調(diào)用Runnable中的run()方法,如果是send()及調(diào)用handlerMessage(Message msg)方法,handlerMessage()方法是空方法,自己重寫進(jìn)行處理
在子線程中使用Handler
在子線程中使用Handler的方式如下
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}