Handler
無論是在代碼的使用中還是面試恨溜,應(yīng)用的頻率都非常高汽久,這就要求我們將Handler
的使用及原理研究透徹指郁。下面根據(jù)博客以及相關(guān)的一些資料,根據(jù)自己對(duì)Handler
的理解迫肖,寫下這篇文章用于記錄锅劝,并加深印象;如有錯(cuò)誤之處蟆湖,請(qǐng)諒解故爵。
看下面這個(gè)Handler
簡(jiǎn)單使用的例子:
private Handler mHandler = new Handler(){//新建Handler并處理消息
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1000:
Log.e("LHC", "Handler Msg.");
break;
default:
break;
}
}
};
new Thread(new Runnable() {//建立子線程,并在線程中發(fā)送消息
@Override
public void run() {
mHandler.sendEmptyMessage(1000);
}
}).start();
這樣就完成了一個(gè)Handler
的簡(jiǎn)單使用隅津。
下面我們來一步步分析诬垂,這個(gè)過程是怎么實(shí)現(xiàn)的。先來看Handler
的創(chuàng)建伦仍,如下:
1 public Handler() {
2 this(null, false);
3 }
4 public Handler(Callback callback, boolean async) {
5 if (FIND_POTENTIAL_LEAKS) {
6 final Class<? extends Handler> klass = getClass();
7 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
8 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
9 }
10 }
11 mLooper = Looper.myLooper();
12 if (mLooper == null) {
13 throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
14 }
15 mQueue = mLooper.mQueue;
16 mCallback = callback;
17 mAsynchronous = async;
18 }
創(chuàng)建過程其實(shí)執(zhí)行的是Handler(Callback callback, boolean async)
方法结窘。來具體看這個(gè)方法。
- 第
5~10
行充蓝,執(zhí)行了一個(gè)if語句隧枫,目的是為了判斷新建的Handler
對(duì)象是否是非靜態(tài)的,如果是就顯示警告谓苟; - 第
11
行官脓,生成了一個(gè)新的Looper
對(duì)象。 - 第
12~14
行涝焙,判斷生成的mLooper
是否為空卑笨,為空是拋出異常,表示進(jìn)行創(chuàng)建Handler
的線程仑撞,此線程還沒有調(diào)用Looper.prepare()
進(jìn)行Looper
初始化赤兴。 - 第
15
行,初始化MessageQueue
對(duì)象mQueue
派草。這個(gè)是將Looper
中的mQueue
賦值給它的搀缠。 - 第
16
行,初始化Callback
接口對(duì)象mCallback
近迁。 - 第
17
行,初始化是否異步標(biāo)記簸州。
第11行代碼鉴竭,生成了一個(gè)新的Looper
對(duì)象,我們看看其內(nèi)部實(shí)現(xiàn)岸浑,代碼如下:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
sThreadLocal
是ThreadLocal<Looper>
對(duì)象搏存,通過get
方法就可獲取線程中對(duì)應(yīng)的Looper
。如果返回值為null
矢洲,就表示線程還沒有關(guān)聯(lián)Looper
璧眠,也就是還沒有初始化一個(gè)Looper
。初始化的方法如下:
1 public static void prepare() {
2 prepare(true);
3 }
4 private static void prepare(boolean quitAllowed) {
5 if (sThreadLocal.get() != null) {
6 throw new RuntimeException("Only one Looper may be created per thread");
7 }
8 sThreadLocal.set(new Looper(quitAllowed));
9 }
這就是Looper
的兩個(gè)初始化方法,具體看下帶參數(shù)的方法:
- 第
5~7
行责静,在if中判斷獲取的Looper
對(duì)象時(shí)否為空袁滥,不為空時(shí),拋出異常灾螃,表示一個(gè)線程只能初始化一個(gè)Looper
题翻。 - 第
8
行,調(diào)用Looper
的構(gòu)造方法生成一個(gè)對(duì)象腰鬼,并將其設(shè)置到sThreadLocal
中嵌赠。
那么在構(gòu)造方法中有進(jìn)行了什么操作呢?看代碼:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在這個(gè)構(gòu)造方法中完成了MessageQueue
的創(chuàng)建熄赡,及當(dāng)前線程的保存姜挺。這樣在調(diào)用new Handler()
時(shí),通過代碼mQueue = mLooper.mQueue;
將此時(shí)新建的mQueue
賦值給了Handler
中的mQueue
了彼硫。
通過上面的解釋炊豪,我們也就明白了為什么在子線程中不能直接new Handler()
,而是需要先進(jìn)行調(diào)用Looper.prepare()
方法了乌助。那么在UI線程中為什么有可以了呢?這是因?yàn)樵?code>ActivityThread類中已經(jīng)初始化了溜在,具體下面在說。
Handler
建立后他托,就是消息的發(fā)送了掖肋,如例子代碼:
mHandler.sendEmptyMessage(1000);
我們進(jìn)入看看其具體的實(shí)現(xiàn),代碼如下:
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
在sendEmptyMessageDelayed(int what, long delayMillis)
方法中赏参,將我們傳入的what
封裝成了一個(gè)Message
信息志笼,然后在傳給方法sendMessageDelayed(msg, delayMillis)
。我們繼續(xù)深入把篓,看代碼:
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);
}
封裝的Message
經(jīng)過傳遞到了Handler中的enqueueMessage(queue, msg, uptimeMillis)
方法纫溃,在方法中給target
進(jìn)行了賦值(這里的target
就是Message
中的Handler
對(duì)象),并進(jìn)行了異步判斷韧掩,最終將Message
傳遞給了方法MessageQueue
類中的enqueueMessage(msg, uptimeMillis)
紊浩。那么在這個(gè)方法中做了哪些處理呢?看代碼:
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
......
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
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; // invariant: p == prev.next
prev.next = msg;
......
}
return true;
}
這個(gè)方法就實(shí)現(xiàn)了將封裝好的Message
消息疗锐,加入到了MessageQueue
消息隊(duì)列當(dāng)中坊谁。MessageQueue是按照Message觸發(fā)時(shí)間的先后順序排列的,隊(duì)頭的消息是將要最早觸發(fā)的消息滑臊。當(dāng)有消息需要加入消息隊(duì)列時(shí)口芍,會(huì)從隊(duì)列頭開始遍歷,直到找到消息應(yīng)該插入的合適位置雇卷,以保證所有消息的時(shí)間順序.
那么我們是在哪里進(jìn)行取消息的呢鬓椭?是在ActivityThread
之中颠猴,我們來看代碼:
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到在ActivityThread
中,我們調(diào)用了Looper.prepareMainLooper();
進(jìn)行了初始化小染,生成了主線程Looper
翘瓮,在通過調(diào)用Looper.loop();
來進(jìn)行消息獲取。代碼如下:
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);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
}
}
在方法中用for
生成了一個(gè)死循環(huán)氧映,在里面循環(huán)調(diào)用queue.next()
獲取Message
春畔,當(dāng)獲取的消息為空時(shí),就跳出死循環(huán)岛都;不為空時(shí)律姨,就通過代碼msg.target.dispatchMessage(msg);
去分配消息。那么在next()
中是如何不斷的獲取消息的臼疫,看代碼:
Message next() {
......
for (;;) {
......
synchronized (this) {
// Try to retrieve the next message. Return if found.
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) {
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.
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;
}
......
}
......
}
}
這個(gè)方法中也是使用for
生成了一個(gè)死循環(huán)择份,用于獲取MessageQueue
隊(duì)列中的消息,消息獲取成功后退出死循環(huán)烫堤,等待下次的調(diào)用荣赶。這樣就完成了循環(huán)取消息的過程。
消息獲取到之后鸽斟,使用代碼msg.target.dispatchMessage(msg);
來進(jìn)行消息分配處理拔创,看具體代碼:
1 public void dispatchMessage(Message msg) {
2 if (msg.callback != null) {
3 handleCallback(msg);
4 } else {
5 if (mCallback != null) {
6 if (mCallback.handleMessage(msg)) {
7 return;
8 }
9 }
10 handleMessage(msg);
11 }
12 }
- 當(dāng)
msg.callback
不為空時(shí),調(diào)用的是靜態(tài)方法handleCallback(msg)
富蓄; - 當(dāng)
msg.callback
為空時(shí)且mCallback
不為空時(shí)剩燥,調(diào)用的是Callback
接口中的handleMessage(msg)
; - 當(dāng)
msg.callback
為空時(shí)且mCallback
為空時(shí),調(diào)用Handler
中的自己的handleMessage(msg)
;
我們通過上面的分析立倍,可以得到這樣的結(jié)論:
-
線程默認(rèn)是沒有Looper的灭红,如果需要使用Handler,就必須先創(chuàng)建Looper口注。
-
ActivityThread被初始化的是時(shí)候就創(chuàng)建了Looper变擒,并運(yùn)行了loop()方法,這也就是UI線程中能直接使用Handler的原因寝志。
-
Handler創(chuàng)建的時(shí)候會(huì)采用當(dāng)前線程的Looper來構(gòu)造消息循環(huán)系統(tǒng)娇斑,Looper在哪個(gè)線程創(chuàng)建,就跟哪個(gè)線程綁定材部,并且Handler是在他關(guān)聯(lián)的Looper對(duì)應(yīng)的線程中處理消息的悠菜。
我們調(diào)用post
方法來實(shí)現(xiàn)更新UI的操作,代碼如下:
postHandler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
//在這里進(jìn)行耗時(shí)操作...
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
postHandler.post(new Runnable() {
@Override
public void run() {//在這里進(jìn)行UI更新
Log.e("LHC", "Handler Post...");
}
});
}
});
在這里需要注意的是:post
方法傳入的是Runnable
败富,看著是新建了一個(gè)子線程,那么在子線程中怎么能進(jìn)行UI更新呢摩窃?首先兽叮,我們要明確一點(diǎn)芬骄,一個(gè)線程的啟動(dòng)需要調(diào)用start()
方法,但是這里并沒有調(diào)用鹦聪。在JAVA
中账阻,直接調(diào)用run()
方法相當(dāng)于調(diào)用了一個(gè)普通方法,當(dāng)然了還是當(dāng)前線程執(zhí)行泽本。我們來看下post
里面代碼的具體實(shí)現(xiàn):
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
方法之中淘太,將參數(shù)r
傳遞給sendMessageDelayed
方法,然后在方法getPostMessage(r)
中规丽,將r
賦值給了m.callback
并封裝成了Message
返回蒲牧。而sendMessageDelayed
方法以后的處理流程在上面已經(jīng)講過了,這里就不在重復(fù)了赌莺。
我們?cè)趤砘剡^頭來看看上面提到的方法dispatchMessage(msg)
冰抢,代碼中當(dāng)msg.callback不為空時(shí),調(diào)用的是靜態(tài)方法handleCallback(msg)
艘狭。那么這個(gè)靜態(tài)方法中執(zhí)行的是什么呢挎扰?看代碼:
private static void handleCallback(Message message) {
message.callback.run();
}
這里調(diào)用的是Message
中callback
下的run()
方法,而這個(gè)callback
就是我們?cè)谡{(diào)用post(Runnable r)
時(shí)的傳入的參數(shù)r
巢音。
所以post
方法中就不存在新建線程遵倦,方法post
運(yùn)行在調(diào)用它的Handler
所在的線程中,而這個(gè)Handler
運(yùn)行在主線程中官撼,所以在主線程中更新UI肯定沒有問題了梧躺。
那么系統(tǒng)為什么不允許在子線程訪問UI呢?摘自<Android 開發(fā)藝術(shù)與探索>
這是因?yàn)锳ndroid的UI線程不是線程安全的歧寺,如果在多線程中并發(fā)訪問可能會(huì)導(dǎo)致UI控件處于不可預(yù)期的狀態(tài)燥狰。
那么系統(tǒng)為什么不對(duì)UI控件加上鎖機(jī)制呢?摘自<Android 開發(fā)藝術(shù)與探索>
缺點(diǎn)有兩個(gè):首先斜筐,加上鎖機(jī)制會(huì)讓UI訪問的邏輯變的復(fù)雜龙致;其次,鎖機(jī)制會(huì)降低UI訪問的效率顷链,因?yàn)殒i機(jī)制會(huì)阻塞某些線程的執(zhí)行目代。
在看下面的代碼:
Handler h = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.e("LHC", "Handler.Callback Msg:"+ msg.what);
return true;
}
});
new Thread(new Runnable() {
@Override
public void run() {
h.sendEmptyMessage(1);
}
}).start();
這里使用的是構(gòu)造方法Handler(Callback callback)
進(jìn)行生成Handler
,具體實(shí)現(xiàn)代碼:
public interface Callback {
public boolean handleMessage(Message msg);
}
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;
}
將傳入的Callback
接口對(duì)象參數(shù)callback
賦值給了Handler
中的成員變量mCallback
嗤练。在回過頭來看處理消息方法dispatchMessage(msg)
榛了,代碼中當(dāng)msg.callback為空時(shí)且mCallback不為空時(shí),調(diào)用的是Callback接口中的handleMessage(msg)
煞抬。
到這里就將分發(fā)消息方法中的情況都介紹清楚了霜大。
為什么Looper.loop()死循環(huán)不會(huì)導(dǎo)致APP ANR?
ActivityThread的main方法主要作用就是做消息循環(huán)革答,一旦退出消息循環(huán)战坤,主線程運(yùn)行完畢曙强,那么你的應(yīng)用也就退出了。
Android是事件驅(qū)動(dòng)的途茫,在Loop.loop()中不斷接收事件碟嘴、處理事件,而Activity的生命周期都依靠于主線程的Loop.loop()來調(diào)度囊卜,所以可想而知它的存活周期和Activity也是一致的
娜扇。當(dāng)沒有事件需要處理時(shí),主線程就會(huì)阻塞栅组;當(dāng)子線程往消息隊(duì)列發(fā)送消息雀瓢,并且往管道文件寫數(shù)據(jù)時(shí),主線程就被喚醒笑窜。
真正會(huì)卡死主線程的操作是在回調(diào)方法onCreate/onStart/onResume等操作時(shí)間過長致燥,會(huì)導(dǎo)致掉幀,甚至發(fā)生ANR排截,looper.loop本身不會(huì)導(dǎo)致應(yīng)用卡死嫌蚤。
上面我們基本講解了Handler
的流程,下面我們整體上來總結(jié)一下:
Handler
機(jī)制主要涉及了這四個(gè)類:Handler
断傲,Lopper
脱吱,Message
,MessageQueue
认罩。
-
新建Handler箱蝠,通過Handler的post或者sendMessage方法發(fā)送消息,Handler通過sendMessageDelayed方法垦垂,將Message交給MessageQueue.
-
MessageQueue通過調(diào)用enqueueMessage(msg, uptimeMillis)方法宦搬,將Message以鏈表的方式加入隊(duì)列當(dāng)中.
-
Looper類中的loop()方法循環(huán)調(diào)用MessageQueue.next()方法獲取消息,并調(diào)用Handler的dispatchMessage(msg)方法處理消息.
-
在dispatchMessage(msg)方法中劫拗,根據(jù)判斷msg.callback间校,mCallback是否為空來執(zhí)行相對(duì)應(yīng)的回調(diào),如果都為空就執(zhí)行Handler的自身handlerMessage(msg)回調(diào).
在使用Handler
過程當(dāng)中页慷,我們還會(huì)遇到這種情況憔足,看代碼:
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//TODO 自己的代碼邏輯
}
};
這次這樣寫的時(shí)候,代碼new Handler()
中的Handler
報(bào)警告The following Handler class should be static or leaks might occur
酒繁,警告的意思也就是說Handler類應(yīng)當(dāng)是靜態(tài)的滓彰,否則可能會(huì)造成內(nèi)存泄露。
那么為什么會(huì)出現(xiàn)這個(gè)警告呢州袒?Handler
會(huì)在一個(gè)后臺(tái)線程中處理耗時(shí)操作揭绑,處理完成后將結(jié)果發(fā)送給UI線程進(jìn)行保存或者顯示。那么在后臺(tái)線程執(zhí)行的過程中郎哭,如果Activity關(guān)閉了洗做,在正常情況下弓叛,Activity會(huì)在GC檢查時(shí)被回收掉,但是由于后臺(tái)線程還在執(zhí)行诚纸,它持有Handler的引用,而Handler持有Activity的引用陈惰,這樣造成Activity不會(huì)被回收掉畦徘,直到后臺(tái)線程執(zhí)行完成。這樣就造成了內(nèi)存泄露
抬闯。那么我們?nèi)绾谓鉀Q呢井辆?看下面代碼:
private static class MyHandler extends Handler{
private WeakReference<Activity> weakReference;
MyHandler(Activity activity){
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity activity = weakReference.get();
if (activity == null || activity.isFinishing()){
return;
}
//TODO msg消息的處理
......
}
}
@Override
protected void onDestroy() {
super.onDestroy();
myHandler.removeCallbacksAndMessages(null);//Activity退出時(shí),將還未執(zhí)行的消息處理掉溶握。免得引發(fā)異常
}
通過上面的這種寫法杯缺,就解決了內(nèi)存泄露的問題。