Handler簡(jiǎn)單使用
1.使用靜態(tài)內(nèi)部類的方式繼承Handler并重寫接受的方法handleMessage。之所以使用靜態(tài)內(nèi)部類,是因?yàn)殪o態(tài)內(nèi)部類不會(huì)持有外部類的引用
static class MyHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 0x11:
String str = (String) msg.obj;
Log.d(TAG,"msg content == "+str);
break;
default:break;
}
}
}
2.獲取Handler實(shí)例
private Handler mHander = new MyHandler();
3.獲取Message對(duì)象
1)Message msg = new Message();
2)Message msg = Message.obtain();/mHander.obtainMessage();等
obtain.what = 0x11;
obtain.obj = "Message Content";
獲取Message方式有2中科盛,建議使用第二種方式寺董,因?yàn)榈诙N方法采用了緩存池機(jī)制,避免重復(fù)創(chuàng)建新的對(duì)象。
4.發(fā)送消息
mHander.sendMessage(msg);立即發(fā)出
mHander.sendMessageDelayed(obtain,time);延時(shí)發(fā)送
5.最后在handleMessage方法中處理消息
子線程創(chuàng)建Handler
在子線程中直接創(chuàng)建Handler會(huì)報(bào)錯(cuò)
錯(cuò)誤日志為:
Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()"
代碼中可以看到 Handler在創(chuàng)建時(shí)會(huì)判斷當(dāng)前線程的Looper
是否為空
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
為空的話則會(huì)報(bào)錯(cuò)。myLooper方法是獲取當(dāng)前線程的Looper,Looper存儲(chǔ)早sThreadLocal
中
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
sThreadLocal是ThreadLocal<Looper>
類型。ThreadLocal中的對(duì)象set/get方法獲取到的數(shù)據(jù)都是當(dāng)前線程的變量痰娱。
當(dāng)myLooper為空時(shí)則說(shuō)明當(dāng)前線程Looper未初始化弃榨,而初始化的方法則是:
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));
}
所以子線程中如果創(chuàng)建Handler則需要調(diào)用Looper.prepare()方法
。而Looper.loop方法則是核心梨睁,消息的傳遞全依賴于此鲸睛。
Handler原理
Handler的運(yùn)行機(jī)制依賴于MessageQueue與Looper的支持。
我們從代碼角度來(lái)看一下這三者之間的關(guān)系:
從入口Handler.sendMessage
方法看起:
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
}
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);
}
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);
}
sendMessage方法鏈看下來(lái)坡贺,最終調(diào)用了mQueue
的enqueueMessage
方法官辈,mQueue是MessageQueue類型,是Looper的成員變量
在Handler初始化方法賦值。下面我們看一下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;
}
從這邊可以看到MessageQueue內(nèi)部存儲(chǔ)采用的是鏈表格式
遍坟,本次方法的作用是將消息插入到鏈尾拳亿。整體邏輯是鏈表為空時(shí),將msg使用mMessages保存下來(lái)愿伴,然后喚醒肺魁。當(dāng)數(shù)據(jù)不為空時(shí),遍歷鏈表將數(shù)據(jù)插入到鏈尾隔节,并喚醒鹅经。
綜上所述Handler的sendMessage方法本質(zhì)上就是一次插入方法,目的是將消息插入到MessageQueue鏈尾官帘。
我們知道在子線程中發(fā)送消息之后則需要調(diào)用Looper.loop
方法瞬雹,否則消息不生效。下面我們來(lái)看一下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 msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}
}
上面是省略了的代碼刽虹,可以看到loop是個(gè)死循環(huán),唯一的退出操作是MessageQueue的next方法返回為空
呢诬。當(dāng)Looper調(diào)用quit或者quitSafely方法的時(shí)候涌哲,next會(huì)返回為空,此時(shí)Looper會(huì)跳出循環(huán)尚镰。next方法是阻塞操作阀圾,當(dāng)沒有消息時(shí),next會(huì)一直阻塞在那里狗唉,導(dǎo)致loop方法也阻塞在那里初烘。直到next方法返回了新消息,Looper就會(huì)處理新消息分俯。當(dāng)獲取到信息時(shí)肾筐,會(huì)通過(guò)msg.target.dispatchMessage(msg)
處理,target就是發(fā)送這條消息的Handler(Handler的enqueueMessage方法中將自身設(shè)置給msg(msg.target = this))缸剪,最終消息鏈回到了Handler的dispatchMessage
方法中:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
msg.callback是Runable
類型吗铐,當(dāng)我們調(diào)用Handler.post(runable)的時(shí)候,最終是將Runable設(shè)置給Message的callback變量杏节。
dispatchMessage判斷msg.callback是否為空唬渗,不為空調(diào)用handleCallback方法:
private static void handleCallback(Message message) {
message.callback.run();
}
就是調(diào)用了Runable的run方法典阵。而callback為空時(shí)則判斷mCallback是否為空,mCallback是Callback類型
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
是Handler提供的一種非侵入式的回調(diào)镊逝,當(dāng)你不想重寫handler時(shí)可以設(shè)置Callback壮啊,在handleMessage處理消息并返回為true。
否則則調(diào)用自身handleMessage方法撑蒜,這個(gè)方法子類重寫后便可以處理消息他巨。
到這里handler調(diào)用原理就走通了,那么有幾個(gè)問(wèn)題减江?
1.View.post Handler.post 和普通的發(fā)送有什么區(qū)別
2.主線程直接創(chuàng)建Handler為什么不報(bào)錯(cuò)
3.loop阻塞了為什么不會(huì)影響主線程染突。
View.post
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
View.post方法判斷有兩條,一如果AttachInfo不為空則直接使用它的handler.post處理Runable辈灼。
如果為空的話份企,則使用HandlerActionQueue.post。HandlerActionQueue是一個(gè)隊(duì)列巡莹,內(nèi)部維護(hù)這一個(gè)數(shù)組司志。
post方法只是將Runable封裝成HandlerAction放入到數(shù)組中
public void post(Runnable action) {
postDelayed(action, 0);
}
public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}
在View的dispatchAttachedToWindow方法中會(huì)調(diào)用HandlerActionQueue的executeActions方法,遍歷數(shù)組通過(guò)Handler.postDelayed處理信息
public void executeActions(Handler handler) {
synchronized (this) {
final HandlerAction[] actions = mActions;
for (int i = 0, count = mCount; i < count; i++) {
final HandlerAction handlerAction = actions[i];
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
mActions = null;
mCount = 0;
}
}
我們之前分析過(guò)Handler的dispatchMessage方法降宅。其中針對(duì)Message的callback是否為空做了條件判定骂远。callback是Runable類型。
Handler.post方法
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;
}
Handler.post方法其實(shí)也是調(diào)用了sendMessageDelayed來(lái)發(fā)送消息腰根,區(qū)別在于在獲取Message的時(shí)候?qū)unable設(shè)置給Message的callback屬性激才,在最終分發(fā)的方法dispatchMessage中依據(jù)callback是否為空來(lái)判定是否Runable的run方法還是Handler自己的回調(diào)方法。
到這邊我們可以解釋了View.post和Handler.post本質(zhì)上并沒有不同额嘿,都是依賴于Handler發(fā)送消息機(jī)制瘸恼,區(qū)別在于最終消息的回調(diào)方法不同。
在子線程中直接創(chuàng)建handler會(huì)報(bào)錯(cuò)册养,主線程直接創(chuàng)建Handler為什么不報(bào)錯(cuò)
Looper在主線程入口函數(shù)中調(diào)用了prepareMainLooper
方法东帅,該方法是是創(chuàng)建主線程的Looper,因此我們?cè)谥骶€程中創(chuàng)建handler時(shí)球拦,Looper已經(jīng)存在靠闭,所以不會(huì)報(bào)錯(cuò)。
Android中為什么主線程不會(huì)因?yàn)長(zhǎng)ooper.loop()里的死循環(huán)卡死坎炼?
Android底層采用的是pipe/epoll
機(jī)制愧膀,當(dāng)主線程的MessageQueue沒有消息時(shí),便阻塞在loop的queue.next方法的nativePullOnce里点弯,此時(shí)主線程會(huì)釋放CPU資源進(jìn)入休眠狀態(tài)扇调,直到下一條消息發(fā)送或者有事務(wù)到達(dá)時(shí),通過(guò)向pipe管道寫端寫入數(shù)據(jù)來(lái)喚醒主線程工作抢肛。這邊采用的epoll機(jī)制是一種IO多路復(fù)用的機(jī)制,可以同時(shí)監(jiān)聽多個(gè)描述符狼钮,當(dāng)某個(gè)描述符就緒(讀/寫就緒)就立即通知相應(yīng)程序進(jìn)行讀或者寫操作碳柱,本質(zhì)同步I/O,即讀寫是阻塞的。因此主線程大多數(shù)情況下處于休眠狀態(tài)熬芜,所以不會(huì)大量消耗CPU資源導(dǎo)致卡死
莲镣。
epoll提供了三種方法
epoll_create(): 創(chuàng)建一個(gè)epoll實(shí)例并返回相應(yīng)的文件描述符(epoll_create1() 擴(kuò)展了epoll_create() 的功能)。
epoll_ctl(): 注冊(cè)相關(guān)的文件描述符使用
epoll_wait(): 可以用于等待IO事件涎拉。如果當(dāng)前沒有可用的事件瑞侮,這個(gè)函數(shù)會(huì)阻塞調(diào)用線程。
詳情參考https://my.oschina.net/u/3863980/blog/1933086
Activity是如何在死循環(huán)中執(zhí)行生命周期方法的鼓拧?
通過(guò)Handler機(jī)制
執(zhí)行生命周期方法的半火。ActivityThread中有內(nèi)部類H繼承Handler。
Activity生命周期依靠looper.loop季俩,當(dāng)Handler接受到消息時(shí)钮糖,在其內(nèi)部handleMessage方法中處理對(duì)應(yīng)的生命周期方法。