0.前言
Handler作為Android代碼編寫(xiě)以及面試時(shí)經(jīng)常遇到的內(nèi)容溉浙,有必要花個(gè)時(shí)間整理一下迹蛤,畢竟寫(xiě)過(guò)的東西印象會(huì)更加深刻民珍。
1.什么是Handler?
1.1 定義
源碼里面撈出來(lái)的內(nèi)容,英文不難看懂盗飒。主要就是說(shuō)每個(gè)Handler會(huì)和每個(gè)線(xiàn)程以及線(xiàn)程對(duì)應(yīng)的消息隊(duì)列相綁定嚷量。之后消息就可通過(guò)Handler在線(xiàn)程之間傳遞。
A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}. Each Handler
instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread 逆趣,message queue of the thread that is creating it -- from that point on,
it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
<p>There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed at some point in the future; and
(2) to enqueue an action to be performed on a different thread than your own.
1.2 Handler的作用
Handler 的作用主要為兩個(gè):
- 讓程序在未來(lái)的某個(gè)時(shí)間點(diǎn)去執(zhí)行一些事情
- 事件跨線(xiàn)程傳遞執(zhí)行(最主要的使用就是子線(xiàn)程讓主線(xiàn)程去做一些刷新的事情)
1.3 Handler的基本用法
1.在未來(lái)某個(gè)時(shí)刻去執(zhí)行runnable中的事件
handler.post(runnable,delayMillis);
2.不同消息間傳遞事件
android.os.Handler handler = new Handler(){
@Override
public void handleMessage(final Message msg) {
//這里接受并處理消息
}
};
//發(fā)送消息
handler.sendMessage(message);
2.Handler基本原理
2.1 Handler原理相關(guān)的類(lèi)
- Handler
- Looper
- MessageQueue
- Messagre
2.2 Handler原理概述
首先放一張Handler消息處理的原理圖
根據(jù)圖一步步梳理Handler實(shí)現(xiàn)機(jī)制
2.3 Handler工作流程
2.3.1 創(chuàng)建Handler
首先第一步是創(chuàng)建Handler,Handler的構(gòu)造函數(shù)主要有如下幾個(gè)
其中的參數(shù)如下
-
boolean async
async 該參數(shù)確定用Handler發(fā)送的消息是否要設(shè)置成異步消息蝶溶,如果為ture設(shè)置為異步消息,異步消息可和同步阻塞一起配合使用,典型的例子是界面的定時(shí)刷新抖所。
-
Callback callback
設(shè)置了該參數(shù)梨州,那么Handler在dispatchMessage回調(diào)時(shí)會(huì)優(yōu)先調(diào)用callback中的代碼,若返回為true,則不再執(zhí)行handleMessage的回調(diào)
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
-
Looper looper
設(shè)置該Hanlder對(duì)應(yīng)的Looper參數(shù)田轧,對(duì)于每個(gè)Handler,都有一個(gè)對(duì)應(yīng)的Looper,每個(gè)Looper有其對(duì)應(yīng)的MessageQueue暴匠。如果沒(méi)有顯式的設(shè)置Handler的Looper,那么Handler默認(rèn)會(huì)取該線(xiàn)程對(duì)應(yīng)的Looper賦給該Handler。
2.3.2 Handler發(fā)送消息
Handler發(fā)送消息主要調(diào)用的是sendMessage方法傻粘。
sendMessage(@NonNull Message msg)
發(fā)送消息的方法有許多不同的名稱(chēng)和參數(shù)每窖。但是,你從源碼一步步看最后都會(huì)指向如下的這段代碼
public boolean sendMessageAtTime(@NonNull 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);
}
看這段代碼我們會(huì)發(fā)現(xiàn)弦悉,其主要的操作就是調(diào)用壓隊(duì)列的方法窒典。傳入的參數(shù)是隊(duì)列,消息以及對(duì)應(yīng)的事件
2.3.3 MessageQueue壓入消息
該方法首先是對(duì)Message 實(shí)例進(jìn)行各種參數(shù)的設(shè)置稽莉,最主要的就是target參數(shù)瀑志,該參數(shù)用于確定消息從MessageQueue中取出后去調(diào)用哪個(gè)Handler的dispatchMessage,從而進(jìn)行消息的處理污秆。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
設(shè)置完參數(shù)劈猪,Handler的enqueueMessage就會(huì)去調(diào)用MessageQueue的enqueueMessage方法,代碼如下
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
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 {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
從代碼中我們能發(fā)現(xiàn)良拼,消息隊(duì)列主要是通過(guò)鏈表的形式進(jìn)行的存儲(chǔ)岸霹。這段代碼的所處理的事情就是根據(jù)Message中的when參數(shù)從列表中來(lái)找到需要插入的點(diǎn),并把Message進(jìn)行參入将饺。
2.3.4 Looper取出消息
Looper取出消息主要是調(diào)用了Looper類(lèi)的loop方法贡避,該方法里面有一個(gè)for(;;)循環(huán),不停的從MessageQueue中取出消息予弧。
for (;;)
{
Message msg = queue.next();
……
}
2.3.5 Handler處理消息
當(dāng)消息取出以后刮吧,即會(huì)調(diào)用
msg.target.dispatchMessage(msg);
來(lái)處理消息,這個(gè)target即時(shí)開(kāi)始enqueMessage時(shí)塞入的Handler掖蛤。
從Handler中我們可以看到杀捻,這dispatchMessage(Message msg)方法。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
其中handleMessage(msg)就是我們自定義Handler時(shí)的回調(diào)蚓庭,這樣消息就走完了整個(gè)流程致讥。
3.Handler延伸
3.1 Q Handler 為什么會(huì)導(dǎo)致內(nèi)存泄漏,該如何處理這個(gè)內(nèi)存泄漏問(wèn)題器赞?
A: Handler的普通用法如下:
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
由于Handler是通過(guò)匿名內(nèi)部類(lèi)的方式實(shí)現(xiàn)的垢袱,所以其會(huì)對(duì)外部類(lèi)有引用(通常為Activity),這就會(huì)引起內(nèi)存泄漏港柜。
通常的解決如下
把Handler定義成靜態(tài)內(nèi)部類(lèi)请契,如Handler中需要對(duì)外部類(lèi)的參數(shù)有引用咳榜,那么可以使用弱引用,示例代碼如下
private static class MyHandler extends Handler{
//持有弱引用HandlerActivity,GC回收時(shí)會(huì)被回收掉.
private final WeakReference<HandlerActivity> mActivty;
public MyHandler(HandlerActivity activity){
mActivty =new WeakReference<HandlerActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity=mActivty.get();
super.handleMessage(msg);
if(activity!=null){
//執(zhí)行業(yè)務(wù)邏輯
}
}
}
3.2 Q Handler類(lèi)中有個(gè)Callback的接口爽锥,請(qǐng)問(wèn)這個(gè)接口有什么用涌韩?
A: 這個(gè)的設(shè)計(jì)主要是為了解決Handler內(nèi)存泄漏的問(wèn)題。
可參考如下代碼:
Handler handler = new Handler(new Handler.Callback(
@Override
public boolean handleMessage(Message msg) {
if (msg.what ==1){
textView.setText("Hello!");
return true;
}
return false;
));
該接口的調(diào)用主要出現(xiàn)在dispatchMessage中氯夷,從此處的代碼可知道臣樱,如果mCallback變量沒(méi)有定義,變量為空腮考,會(huì)走Handler系統(tǒng)的方法hadlerMessage(Message msg),而如果mCallback變量定義了擎淤,那么其會(huì)運(yùn)行到mCallback
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
3.3 Q Handler中的普通消息,異步消息和消息屏障是什么關(guān)系秸仙?有什么作用?
MessageQueue中的Message一共有三種類(lèi)型桩盲,即普通消息寂纪,異步消息和消息屏障。區(qū)別是消息屏障它的target為null赌结。而普通消息和異步消息是通過(guò)setAsynchronous為true來(lái)進(jìn)行區(qū)分的捞蛋。
在設(shè)置消息屏障后,異步消息具有優(yōu)先處理的權(quán)利柬姚。界面的定時(shí)刷新就是用的這個(gè)機(jī)制
3.4 Q Message的target是什么時(shí)候注入的拟杉?
撈取Handler的源碼發(fā)現(xiàn)如下這段
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
即哪個(gè)Handler把Message存入的消息隊(duì)列,最后回調(diào)的就是這個(gè)Handler對(duì)應(yīng)的dispatchMessage 方法量承。
3.5 Q Looper會(huì)不停的從消息隊(duì)列中取消息嗎搬设?
不會(huì),若MessageQueue中消息已經(jīng)取完或者消息要在之后的某個(gè)事件才會(huì)促發(fā)撕捍,則會(huì)調(diào)用native方法 nativePollOnce(long ptr, int timeoutMillis)拿穴,使主線(xiàn)程處于等待狀態(tài)。當(dāng)調(diào)用了往消息隊(duì)列塞入消息時(shí)忧风,則會(huì)調(diào)用nativeWake(long ptr)方法喚醒主線(xiàn)程默色,所以盡管,loop方法中有個(gè)for死循環(huán)狮腿,但是這不會(huì)導(dǎo)致Looper不停從消息隊(duì)列取數(shù)據(jù)腿宰。
3.6 Q Handler的post(Runnable r) 方法是如何實(shí)現(xiàn)Runnable中事件的執(zhí)行的?
post方法從本質(zhì)上來(lái)說(shuō)也是用的Message消息傳遞的流程缘厢。
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
從如下兩端代碼可看出其是對(duì)runnable進(jìn)行了封裝吃度,把runnable塞進(jìn)了Message中。而當(dāng)Handler獲得消息后贴硫,其會(huì)取出msg.callback參數(shù)规肴,然后運(yùn)行里面的runnable方法。
3.7 Q 平時(shí)代碼編寫(xiě)中如何獲得Message對(duì)象比較好?
用Message.obtain()獲取比較好拖刃,避免了gc導(dǎo)致的性能消耗删壮。
3.8 Q IdleHandler是什么,有什么作用兑牡?
IdleHandler是在主線(xiàn)程消息隊(duì)列空閑時(shí)央碟,會(huì)被取出執(zhí)行的對(duì)象【可在一些頁(yè)面優(yōu)化的情景下使用亿虽。