如需轉(zhuǎn)載請評論或簡信例嘱,并注明出處,未經(jīng)允許不得轉(zhuǎn)載
目錄
前言
之前寫過一篇文章Android消息機(jī)制(Handler由境、Looper棚亩、MessageQueue),這篇文章主要是從Java層入手虏杰,分析了和我們實際開發(fā)最貼近的消息機(jī)制原理讥蟆。自認(rèn)為對java層的消息機(jī)制流程已經(jīng)非常熟悉了,但是面試是被問到“Handler是如何實現(xiàn)延遲消息的纺阔?”瘸彤,“為什么Looper.loop()中的死循環(huán)不會導(dǎo)致ANR?”這些問題的時候笛钝,還是不能完全說出個所以然來质况,所以就決定再去看看native層到底做了哪些事情。如果你對java層的消息機(jī)制還不是特別熟悉的話玻靡,建議先看上一篇文章哦
消息隊列的創(chuàng)建
從上一章中结榄,我們知道MessageQueue
是在Looper
的構(gòu)造方法中創(chuàng)建的,但是我們沒有對MessageQueue
做更深入的分析
Looper.java
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
跟進(jìn)一下MessageQueue
的構(gòu)造方法囤捻,看看里面做了什么
MessageQueue.java
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
這里有一個nativeInit()
方法臼朗,這是一個native層的方法
core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
...
//增加引用計數(shù)
nativeMessageQueue->incStrong(env);
//使用強制類型轉(zhuǎn)換符reinterpret_cast把NativeMessageQueue指針強轉(zhuǎn)成long類型并返回到j(luò)ava層
return reinterpret_cast<jlong>(nativeMessageQueue);
}
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
//從當(dāng)前線程的本地緩存(相當(dāng)于ThreadLocal)拿到looper
mLooper = Looper::getForThread();
if (mLooper == NULL) {
//如果looper == null,創(chuàng)建一個looper加入到線程本地緩存中
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
我們發(fā)現(xiàn)蝎土,native層中也會創(chuàng)建一個Looper
和MessageQueue
视哑,且native層的Looper
創(chuàng)建和獲取方式和java層非常的相似。native層的Looper
與Java層的Looper
沒有必然的關(guān)系誊涯,只是在native層重實現(xiàn)了一套類似功能的邏輯
system/core/libutils/Looper.cpp
再來看看Looper
的構(gòu)造方法做了什么
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
//創(chuàng)建fd(文件操作符)
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
strerror(errno));
AutoMutex _l(mLock);
//重建管道
rebuildEpollLocked();
}
這個eventfd實際上就是一個文件描述符挡毅,那什么是文件描述符呢?
文件描述符:簡稱fd醋拧,它就是一個int值慷嗜,又叫做句柄淀弹,在Linux中,打開或新建一個文件庆械,它會返回一個文件描述符薇溃,讀寫文件需要使用文件描述符來指定待讀寫的文件,所以文件描述符就是指代被打開的文件缭乘,所有對這個文件的IO操作都要通過文件描述符
void Looper::rebuildEpollLocked() {
//關(guān)閉舊的管道
if (mEpollFd >= 0) {
close(mEpollFd);
}
//創(chuàng)建一個新的epoll文件描述符沐序,并注冊wake管道
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event));
//設(shè)置mWakeEventFd的可讀事件(EPOLLIN)監(jiān)聽
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
//將喚醒事件fd(mWakeEventFd)添加到epoll文件描述符(mEpollFd),進(jìn)行監(jiān)聽
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
//將各種事件堕绩,如鍵盤策幼、鼠標(biāo)等事件的fd添加到epoll文件描述符(mEpollFd),進(jìn)行監(jiān)聽
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
request.fd, strerror(errno));
}
}
}
}
rebuildEpollLocked()
方法的主要作用其實就是通過epoll機(jī)制設(shè)置了mWakeEventFd的可讀事件和其他各種事件的監(jiān)聽
epoll機(jī)制是Linux最高效的I/O復(fù)用機(jī)制奴紧,使用一個文件描述符管理多個描述符
I/O復(fù)用機(jī)制:多人聊天室就是一種非常適合I/O多路復(fù)用的例子特姐,可能會同時有很多個用戶登錄,但是不會同時在同一個時刻發(fā)言。如果用普通I/O模型,則需要開很多的線程黍氮,大部分線程是空閑的,而且在處理多個客戶的消息的時候需要切換線程唐含,對系統(tǒng)來講也是比較重的。而使用I/O多路復(fù)用則可以重復(fù)使用一條線程,減少線程空閑和切換的情況
epoll模型主要就是三個部分
epoll_create:創(chuàng)建epoll文件描述符
epoll_ctl:添加文件描述符監(jiān)聽(如監(jiān)聽mWakeEventFd的EPOLLIN事件)
epoll_wait:阻塞監(jiān)聽add進(jìn)來的描述符沫浆,只要其中任意一個或多個描述符可用或者超時就會返回(這個我們稍后會介紹)
在進(jìn)一步分析消息機(jī)制的native層原理之前捷枯,我們可以先畫一個基本的系統(tǒng)架構(gòu)圖,捋一捋他們之間的關(guān)系
發(fā)送消息
我們應(yīng)用層調(diào)用Handler
的sendmessage()
专执、sendEmptyMessageDelayed()
淮捆、postDelayed()
、post()
最終都會調(diào)用到MessageQueue#enqueueMessage()
MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
....
//將消息延遲時間賦值給msg的成員變量
msg.when = when;
Message p = mMessages;
boolean needWake;
//① p == null,代表消息隊列為空
//② when == 0本股,當(dāng)我們調(diào)用sendMessageAtFrontOfQueue的時候
//③ when < p.when攀痊,新來的消息比消息隊列中第一個消息還早
if (p == null || when == 0 || when < p.when) {
//插到頭結(jié)點
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//如果不能插到頭結(jié)點,按時間將消息進(jìn)行排序
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;
prev.next = msg;
}
if (needWake) {
//發(fā)送一個eventFd可讀事件
nativeWake(mPtr);
}
}
return true;
}
從這個函數(shù)中我們了解到痊末,所謂的延遲消息蚕苇,并不是延遲去發(fā)送消息,而是延遲去處理消息凿叠。延遲消息和普通消息一樣涩笤,都會在第一時間發(fā)送到消息隊列中
core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
system/core/libutils/Looper.cpp
void Looper::wake() {
uint64_t inc = 1;
//使用write函數(shù)往mWakeEventFd寫入字符inc,epoll就會收到可讀事件盒件,被喚醒
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
...
}
還記得這個mWakeEventFd
是什么嗎蹬碧?它就是我們一開始在native Looper
的構(gòu)造方法中創(chuàng)建的用于可讀事件通知的文件操作符
總結(jié)一下發(fā)送消息就是根據(jù)消息的時間戳順序,往消息隊列中插入消息炒刁,每插入一條消息恩沽,就會往mWakeEventFd寫入字符inc,epoll就會收到可讀事件翔始,被喚醒
接下來我們看看epoll收到可讀事件后罗心,會發(fā)生什么里伯?
讀取消息
Looper.loop()
內(nèi)部實際上調(diào)用的是MessageQueue#next()
來讀取消息
MessageQueue.java
這里最重要的就是nativePollOnce()
方法,他是一個native層方法渤闷,它會一直阻塞疾瓮,這里面就包含了epoll接收可讀事件的相關(guān)邏輯
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
...
//nextPollTimeoutMillis表示nativePollOnce的超時時間
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//這是個native層方法,會一直阻塞飒箭,直到下一條可用的消息返回
//ptr就是NativeMessageQueue的指針
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
//如果消息觸發(fā)時間還沒到狼电,計算還需要等多久
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//如果觸發(fā)時間到了,取出消息
mMessages = msg.next;
msg.next = null;
return msg;
}
} else {
//nextPollTimeoutMillis設(shè)置成-1代表沒有消息弦蹂,nativePollOnce就會無限等待
nextPollTimeoutMillis = -1;
}
...
}
}
core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
system/core/libutils/Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
...
//當(dāng)result不等于0時肩碟,就會跳出循環(huán),返回到j(luò)ava層
if (result != 0) {
return result;
}
//處理內(nèi)部輪詢
result = pollInner(timeoutMillis);
}
}
該方法內(nèi)部是一個死循環(huán)凸椿,核心在于調(diào)用了pollInner
方法削祈,pollInner
方法返回一個int值result,代表著本次輪詢是否成功處理了消息削饵,當(dāng)result不等于0時岩瘦,就會跳出循環(huán),返回到j(luò)ava層繼續(xù)處理java層消息
接下來我們重點看一下pollInner()
做了什么窿撬,這是整個消息機(jī)制的核心
system/core/libutils/Looper.cpp
int Looper::pollInner(int timeoutMillis) {
//...
//事件集合(eventItems),EPOLL_MAX_EVENTS為最大事件數(shù)量叙凡,它的值為16
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
//1.epoll_wait有四種情況
//① 當(dāng)timeoutMillis == -1或timeoutMillis > 0劈伴,進(jìn)入休眠等待
//② 如果有事件發(fā)生,且timeoutMillis == 0握爷,就從管道中讀取事件放入事件集合(eventItems)返回事件個數(shù)
//③ 如果休眠timeoutMillis時間后還沒有被喚醒跛璧,就會返回0
//④ 如果出錯,就會返回-1
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
....
//獲取鎖
mLock.lock();
//2.遍歷事件集合(eventItems)
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
//如果文件描述符為mWakeEventFd
if (fd == mWakeEventFd) {
//并且事件類型為EPOLLIN(可讀事件)新啼,這說明當(dāng)前線程關(guān)聯(lián)的管道的另外一端寫入了新數(shù)據(jù)
if (epollEvents & EPOLLIN) {
//調(diào)用awoken方法不斷的讀取管道數(shù)據(jù)
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
//如果是其他文件描述符追城,就進(jìn)行它們自己的處理邏輯
...
}
}
//3、下面是處理Native的Message
Done:;
mNextMessageUptime = LLONG_MAX;
//mMessageEnvelopes是一個Vector集合燥撞,它代表著native中的消息隊列
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
//取出MessageEnvelope座柱,MessageEnvelop有收件人Hanlder和消息內(nèi)容Message
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
//判斷消息的執(zhí)行時間
if (messageEnvelope.uptime <= now) {//消息到達(dá)執(zhí)行時間
{
//獲取native層的Handler
sp<MessageHandler> handler = messageEnvelope.handler;
//獲取native層的消息
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
//釋放鎖
mLock.unlock();
//通過MessageHandler的handleMessage方法處理native層的消息
handler->handleMessage(message);
}
mLock.lock();
mSendingMessage = false;
//result等于POLL_CALLBACK,表示某個監(jiān)聽事件被觸發(fā)
result = POLL_CALLBACK;
} else {//消息還沒到執(zhí)行時間
mNextMessageUptime = messageEnvelope.uptime;
//跳出循環(huán)物舒,進(jìn)入下一次輪詢
break;
}
}
//釋放鎖
mLock.unlock();
...
return result;
}
總結(jié)一下色洞,pollInner
主要做了三件事情:
- 執(zhí)行epoll_wait方法,等待事件發(fā)生或者超時(重要)
1)當(dāng)timeoutMillis == -1或timeoutMillis > 0冠胯,進(jìn)入休眠等待
2)如果有事件發(fā)生火诸,且timeoutMillis == 0,就從管道中讀取事件放入事件集合(eventItems)返回事件個數(shù)
3)如果休眠timeoutMillis時間后還沒有被喚醒荠察,就會返回0
4)如果出錯置蜀,就會返回-1
- 遍歷事件集合(eventItems)奈搜,檢測哪一個文件描述符發(fā)生了IO事件
遍歷事件集合中,如果是mWakeEventFd
盯荤,就調(diào)用awoken方法不斷的讀取管道數(shù)據(jù)馋吗,直到清空管道,如果是其他的文件描述符發(fā)生了IO事件廷雅,讓它們自己處理相應(yīng)邏輯
- 處理native層的Message
只要epoll_wait
方法返回后耗美,都會進(jìn)入Done
標(biāo)記位的代碼段,就開始處理處理native層的Message
航缀,處理完native層消息后商架,又會返回到j(luò)ava層處理java層的消息
用一個圖總結(jié)一下前面的過程
到此為止,native源碼分析的差不多了芥玉,對于native層消息機(jī)制蛇摸,我們已經(jīng)具備了不錯的理論基礎(chǔ),接下來開始回答文章一開始的問題吧
如何實現(xiàn)延遲消息
我們先來看看應(yīng)用層是如何調(diào)用的灿巧,這個大家應(yīng)該都非常清楚
SystemClock.uptimeMillis()
是從開機(jī)到當(dāng)前的毫秒數(shù)赶袄,也就是說最終傳給Looper::pollInner(int timeoutMillis)
的時間是以開機(jī)時間為基準(zhǔn)的一個時間戳
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
我們上面介紹epoll_wait的時候,說過這個方法的四種情況
1)當(dāng)timeoutMillis == -1或timeoutMillis > 0抠藕,進(jìn)入休眠等待
2)如果有事件發(fā)生饿肺,且timeoutMillis == 0,就從管道中讀取事件放入事件集合(eventItems)返回事件個數(shù)
3)如果休眠timeoutMillis時間后還沒有被喚醒盾似,就會返回0
4)如果出錯敬辣,就會返回-1
所以當(dāng)epoll收到一條延遲消息的時候,timeoutMillis > 0零院,所以就符合第一條溉跃,這時候就會epoll進(jìn)入休眠等待,直到休眠了timeoutMillis的時間告抄,就會被喚醒并返回撰茎。也就是說此時nativePollOnce不再阻塞,就會從消息隊列中取出消息進(jìn)行分發(fā)打洼。所以龄糊,消息延遲就是基于epoll_wait的超時時間來實現(xiàn)的
為什么Looper.loop()中的死循環(huán)不會導(dǎo)致ANR
首先,我們得需要知道ANR是怎么產(chǎn)生的拟蜻,有的人可能會說主線程執(zhí)行了耗時操作绎签、Activity
的最長執(zhí)行時間是5秒、BroadcastReceiver
的最長執(zhí)行時間則是10秒酝锅、Service
的最長執(zhí)行時間是20秒等等這些诡必。但是我們今天要說的,其實是要更深一步
ANR是怎么產(chǎn)生的
ANR的時候,我們會看到系統(tǒng)會彈出一個對話框爸舒,我們先找到這個對話框是從哪里彈出來的
ActivityManagerService.java
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
ActivityRecord parent, boolean aboveSystem, final String annotation) {
...
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
mService.mUiHandler.sendMessage(msg);
}
這里的SHOW_NOT_RESPONDING_UI_MSG
消息最終會執(zhí)行到AppErrors#handleShowAnrUi()
蟋字,從而打開一個AppNotRespondingDialog
AppErrors.java
void handleShowAnrUi(Message msg) {
...
Dialog dialogToShow = new AppNotRespondingDialog(mService, mContext, data);
...
dialogToShow.show()
}
接下來我們來看看這個超時是如何觸發(fā)的,這里拿Service舉例
ActiveService.java
這是Service
開始啟動的方法
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg){
...
bumpServiceExecutingLocked(r, execInFg, "create")
...
app.thread.scheduleCreateService(...)
...
}
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
...
scheduleServiceTimeoutLocked(r.app);
...
}
static final int SERVICE_TIMEOUT = 20*1000;
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
...
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
//發(fā)送一個超時的消息扭勉,這個超時消息如果最終被接收鹊奖,將會執(zhí)行appNotResponding()
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
通過看源碼我們發(fā)現(xiàn),實際上就是在Service
啟動的時候涂炎,會往消息隊列中發(fā)送一個20s的延遲消息忠聚。如果這個消息在20s之內(nèi)沒有被remove(),那就會彈出ANR的彈窗唱捣!為了不彈出這個ANR的彈窗两蟀,如果Android系統(tǒng)交給你們設(shè)計,你們會怎么做呢震缭?
我想你們應(yīng)該很容易想到吧赂毯,Andorid系統(tǒng)在Service啟動完成后就會remove消息,同樣的道理拣宰,輸入事件党涕,BroadcastReceiver啟動等都會發(fā)送一個延遲消息,之后等到成功響應(yīng)后就會remove這個延遲消息
我們通過兩個圖來對比一下啟動Service正常的情況和發(fā)生ANR的情況
looper為什么不會ANR
了解了ANR發(fā)生的原理巡社,我們再來回答looper()
為什么不會ANR應(yīng)該會更準(zhǔn)確一些
我認(rèn)為這里要回答兩點膛堤,一點是epoll的原理,還有一點就是ANR的觸發(fā)原理
在Linux中晌该,文件骑祟、socket、管道(pipe)等可以進(jìn)行IO操作的對象都可以稱之為流气笙,既然是IO流,那肯定會有兩端:read端和write端怯晕,我們可以創(chuàng)建兩個文件描述符wiretFd和readFd潜圃,對應(yīng)read端和write端,當(dāng)流中沒有數(shù)據(jù)時舟茶,讀線程就會阻塞(休眠)等待谭期,當(dāng)寫線程通過wiretFd往流的wiret端寫入數(shù)據(jù)后,readFd對應(yīng)的read端就會感應(yīng)到吧凉,喚醒讀線程讀取數(shù)據(jù)隧出,大概就是這樣的一個讀寫過程。讀線程進(jìn)入阻塞后阀捅,并不會消耗CPU時間胀瞪,這是epoll機(jī)制高效的原因之一
Activity
啟動事件時一個Message
,ANR也是一個延遲Message
,當(dāng)Activity
啟動完成后就移除ANR的Message
凄诞,這是多么自然的事情圆雁,怎么會導(dǎo)致ANR呢?
其他問題
主線程中進(jìn)行耗時操作一定會ANR嗎
通過閱讀源碼可以找到四種ANR類型(下面推薦了四篇文章帆谍,感興趣的可以跟著代碼看一看)
-
Activity
對輸入事件在5秒內(nèi)沒有處理完畢(https://blog.csdn.net/chewbee/article/details/72670038) -
BroadcastReceiver
的onReceive
函數(shù)時10秒內(nèi)沒有執(zhí)行完畢(https://blog.csdn.net/chewbee/article/details/72672095) -
Service
的各個生命周期函數(shù)時20秒內(nèi)沒有執(zhí)行完畢(https://blog.csdn.net/chewbee/article/details/72671665) -
ContentProvider
執(zhí)行相關(guān)操作的超時時間是20s(https://blog.csdn.net/chewbee/article/details/72670603)
如果觸發(fā)了上面這些情景伪朽,就會發(fā)生ANR,反之即使在主線程做了耗時操作汛蝙,你也看不到ANR彈窗
可以做下面這個實驗
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
這樣僅僅會讓這個Activtiy延遲了10s才真正啟動,但是由于Activity沒有接收輸入時間窖剑,所以不會出現(xiàn)ANR彈窗讯私。但是如果在Service中sleep超過20秒,就會出現(xiàn)ANR彈窗稼跳,這里Service和Activity其實是有區(qū)別的
總結(jié)
本文分析了native層MessageQueue
骤星、Looper
的創(chuàng)建過程,也通過了解epoll的喚醒和阻塞機(jī)制翠储,解決了“Handler是如何實現(xiàn)延遲消息的绘雁?”,“為什么Looper.loop()中的死循環(huán)不會導(dǎo)致ANR援所?”這兩個問題庐舟。看源碼有時候雖然辛苦住拭,但是看完之后感覺還是有不少收獲的挪略。所以大家以后有什么非常不解的問題,不妨研究研究源碼吧