您好,登錄后才能下訂單哦!
大家應(yīng)該都知道,Android 的消息機制是基于 Handler 實現(xiàn)的。還記得一年前的自己就看了幾篇博客,知道了 Handler、Looper、MessageQueue 就自以為了解了 Handler 的原理。但其實看源碼的過程中慢慢就會發(fā)現(xiàn),Handler 的內(nèi)容可不止這點, 像同步屏障、 Handler 的 native 層的阻塞喚醒機制等等這些知識以前就沒有理解清楚。因此寫下這篇文章,從頭開始重塑對 Handler 的印象。
覺得文章太長的可以找我拿了完整的PDF自行研究
參考:
(更多完整項目下載。未完待續(xù)。源碼。圖文知識后續(xù)上傳github。)
可以點擊關(guān)于我聯(lián)系我獲取完整PDF
(VX:mm14525201314)
Handler 采用的是一種生產(chǎn)者-消費者模型,Handler 就是生產(chǎn)者,通過它可以生產(chǎn)需要執(zhí)行的任務(wù)。而 Looper
則是消費者,不斷從 MessageQueue
中取出 Message 對這些消息進行消費,下面我們看一下其具體的實現(xiàn)。
首先我們都知道,Handler 對外主要有兩種方式來實現(xiàn)在其所在 Looper
所在線程執(zhí)行指定 Runnable
——post 及 sendMessage
,它們都有對應(yīng)的 delay 方法 。而不論是 post 還是 sendMessage
,都會調(diào)用到 sendMessageDelayed
方法。比如下面是 post 方法的實現(xiàn):
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
可以看到它其實調(diào)用的仍然是 sendMessageDelayed
方法,只是通過 getPostMessage
方法將這個 Runnable
包裝成了一個 Message 對象。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
這個包裝出來的 Message 將 callback 設(shè)置為了對應(yīng)的 Runnable。
而所有的 sendMessage
和 post 方法,實際上最后都通過 sendMessageDelayed
方法調(diào)用到了 sendMessageAtTime
方法:
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
在 sendMessageAtTime
中,它首先通過 mQueue
拿到了對應(yīng)的 MessageQueue
對象,然后調(diào)用了 enqueueMessage
方法將 Message 發(fā)送至 MessageQueue
中。
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);
}
最后實際上是調(diào)用到了 MessageQueue
的 enqueueMessage
方法將這個消息傳入了 MessageQueue
。它將 Message 的 target 設(shè)置為了當(dāng)前的 Handler,同時要注意看到,這里在 enqueueMessage
之前先判斷了一下 mAsynchronous
是否為 true,若為 true 則將該 Message 的 Asynchronous 置為 true。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
那這個 mAsynchronous
是什么時候被賦值的呢?點進去看可以發(fā)現(xiàn)它的賦值是在 Handler 的構(gòu)造函數(shù)中進行的。也就是說創(chuàng)建的 Handler 時若將 async
置為 true 則該 Handler 發(fā)出的 Message 都會被設(shè)為 Async
,也就是『異步消息』。
public Handler(Callback callback, boolean async)
public Handler(Looper looper, Callback callback, boolean async)
關(guān)于異步消息和同步消息是什么,我們放在后面討論。
Handler 有很多種構(gòu)造函數(shù),但其他的構(gòu)造函數(shù)最后仍然會調(diào)用到上述的兩種構(gòu)造函數(shù),其 async 默認會被設(shè)置為 false。
讓我們看看上述的兩種構(gòu)造函數(shù):
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
}
可以看到這個構(gòu)造函數(shù)主要是對 mLooper
、mQueue
、mCallback
、mAsynchronous
進行賦值,其中 mLooper
是通過 Looper.myLooper
方法獲取到的,另一種構(gòu)造函數(shù)除了 Looper
是通過外部傳入以外和這個構(gòu)造函數(shù)的實現(xiàn)差不多。同時我們還能看出,mQueue
這個 MessageQueue
是 Looper
對象內(nèi)部的一個成員變量。
我們接著看看 Handler 發(fā)送了消息后 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
實際上里面維護了一個 Message 構(gòu)成的鏈表,每次插入數(shù)據(jù)都會按時間順序進行插入,也就是說 MessageQueue
中的 Message 都是按照時間排好序的,這樣的話就使得循環(huán)取出 Message 的時候只需要一個個地從前往后拿即可,這樣 Message 都可以按時間先后順序被消費。
最后在需要喚醒的情況下會調(diào)用 nativeWake
這個 native 方法用于進行喚醒,這些和喚醒機制有關(guān)的代碼我們后面再進行討論,先暫時放在一邊。
那么我們看看 Looper.myLooper
方法是如何獲取到 Looper
對象的呢?
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看出來,這個 Looper 對象是通過 sThreadLocal.get 方法獲取到的,也就是說這個 Looper 是一個線程獨有的變量,每個線程具有一個不同的 Looper。
那么這個 Looper
對象是何時創(chuàng)建又何時放入這個 ThreadLocal
中的呢?
我們通過跟蹤可以發(fā)現(xiàn)它實際上是通過 Looper.prepare
方法放入 ThreadLocal
中的:
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));
}
那按道理來說我們在應(yīng)用程序中并沒有調(diào)用 Looper.prepare
方法,為何還能通過主線程的 Handler 發(fā)送 Message 到主線程呢?
其實這個 Looper.prepare
方法在主線程創(chuàng)建時就已經(jīng)被創(chuàng)建并調(diào)用了 prepare 方法進行設(shè)置,具體我們可以看到 ActivityThread
類的 main 函數(shù):
public static void main(String[] args) {
// ...
Looper.prepareMainLooper();
// ...
Looper.loop();
// ...
}
這個 main 函數(shù)其實就是我們進程的入口,可以看出來它首先調(diào)用了 Looper.prepareMainLooper
創(chuàng)建了主線程的 Looper
并傳入 ThreadLocal
,自此我們就可以在主線程發(fā)送消息了。為什么要這樣設(shè)計呢?因為其實我們的 View 繪制事件等都是通過主線程的 Handler 來進行調(diào)度的。
我們接著看到 Looper.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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ...
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
// ...
}
// ...
msg.recycleUnchecked();
}
}
這里其實是一個死循環(huán),它的主要作用是遍歷 MessageQueue
,獲取到 Looper
及 MessageQueue
后,不斷通過 MessageQueue
的 next 方法獲取到消息列表中的下一個 Message,之后調(diào)用了 Message 的 target 的 dispatchMessage
方法對 Message 進行消費,最后對 Message 進行了回收。
通過上面的代碼可以看出,Looper
主要的作用是遍歷 MessageQueue
,每找到一個 Message 都會調(diào)用其 target 的dispatchMessage
對該消息進行消費,這里的 target 也就是我們之前發(fā)出該 Message 的 Handler。
我們接著看到消息的遍歷過程,它不斷地從 MessageQueue
中調(diào)用 next 方法拿到消息,并對其進行消費,那我們具體看看 next 的過程:
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 1
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 2
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 3
if (now < msg.when) {
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;
}
if (mQuitting) {
dispose();
return null;
}
// 4
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
上面的代碼比較長,我們一步步來進行分析
首先在 next 方法內(nèi),它在不斷地進行著循環(huán),在 1 處它先調(diào)用了一次 nativePollOnce
這個 native 方法,它與 Handler 的阻塞喚醒機制有關(guān),我們后面再進行介紹。
之后,在 2 處,它進行了一個非常特殊的處理。這里判斷當(dāng)前的消息是否是 target 為 null 的消息,若 target 為 null,則它會不斷地向下取 Message,直到遇到一個異步的消息。到這里可能會有讀者覺得很奇怪了,明明在 enqueueMessage
中避免了 Message 的 target 為 null,為什么這里還會存在 target 為 null 的消息呢?其實這與 Handler 的同步屏障機制有關(guān),我們稍后介紹
之后便在注釋 3 處判斷判斷當(dāng)前消息是否到了應(yīng)該發(fā)送的時間,若到了應(yīng)該發(fā)送的時間,就會將該消息取出并返回,否則僅僅是將 nextPollTimeoutMillis
置為了剩余的時間(這里為了防止 int 越界做了防越界處理)
之后在注釋 4 處,第一次循環(huán)的前提下,若 MessageQueue
為空或者消息未來才會執(zhí)行,則會嘗試去執(zhí)行一些 idleHandler
,并在執(zhí)行后將 pendingIdleHandlerCount
置為 0 避免下次再次執(zhí)行。
若這一次拿到的消息不是現(xiàn)在該執(zhí)行的,那么會再次調(diào)用到 nativePollOnce
,并且此次的 nextPollTimeoutMillis
不再為 0 了,這與我們后面會提到的阻塞喚醒機制有關(guān)。
消息的處理是通過 Handler 的 dispatchMessage
實現(xiàn)的:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
它優(yōu)先調(diào)用了 Message 的 callback,若沒有 callback 則會調(diào)用 Handler 中 Callback 的 handleMessage
方法,若其仍沒定義則最終會調(diào)用到 Handler 自身所實現(xiàn)的 handleMessage
方法。
因此我們在使用的時候可以根據(jù)自己的需求來重寫上面三者其中一個。
Handler 中存在著一種叫做同步屏障的機制,它可以實現(xiàn)異步消息優(yōu)先執(zhí)行的功能,讓我們看看它是如何實現(xiàn)的。
在 Handler 中還存在了一種特殊的消息,它的 target 為 null,并不會被消費,僅僅是作為一個標(biāo)識處于 MessageQueue
中。它就是 SyncBarrier
(同步屏障)這種特殊的消息。我們可以通過 MessageQueue
::postSyncBarrier
方法將其加入消息隊列。
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
可以看到,這里并沒有什么特殊的,只是將一個 target 為 null 的消息加入了消息隊列中,但我們在前面的 enqueueMessage
方法中也看到了,普通的 enqueue
操作是沒有辦法在消息隊列中放入這樣一個 target 為 null 的消息的。因此這種同步屏障只能通過這個方法發(fā)出。
我們可以通過 removeSyncBarrier
方法來移除消息屏障。
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
// 找到 target 為 null 且 token 相同的消息
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
這里主要是將同步屏障從 MessageQueue
中移除,一般執(zhí)行完了異步消息后就會通過該方法將同步屏障移除。
最后若需要喚醒,調(diào)用了 nativeWake
方法進行喚醒。
而看了前面 MessageQueue::next
的代碼我們知道,當(dāng) MessageQueue
中遇到了一個同步屏障,則它會不斷地忽略后面的同步消息直到遇到一個異步的消息,這樣設(shè)計的目的其實是為了使得當(dāng)隊列中遇到同步屏障時,則會使得異步的消息優(yōu)先執(zhí)行,這樣就可以使得一些消息優(yōu)先執(zhí)行。比如 View 的繪制過程中的 TraversalRunnable
消息就是異步消息,在放入隊列之前先放入了一個消息屏障,從而使得界面繪制的消息會比其他消息優(yōu)先執(zhí)行,避免了因為 MessageQueue
中消息太多導(dǎo)致繪制消息被阻塞導(dǎo)致畫面卡頓,當(dāng)繪制完成后,就會將消息屏障移除。
從前面可以看出來 Handler 中其實還存在著一種阻塞喚醒機制,我們都知道不斷地進行循環(huán)是非常消耗資源的,有時我們 MessageQueue
中的消息都不是當(dāng)下就需要執(zhí)行的,而是要過一段時間,此時如果 Looper
仍然不斷進行循環(huán)肯定是一種對于資源的浪費。因此 Handler 設(shè)計了這樣一種阻塞喚醒機制使得在當(dāng)下沒有需要執(zhí)行的消息時,就將 Looper
的 loop 過程阻塞,直到下一個任務(wù)的執(zhí)行時間到達或者一些特殊情況下再將其喚醒,從而避免了上述的資源浪費。
這個阻塞喚醒機制是基于 Linux 的 I/O 多路復(fù)用機制 epoll
實現(xiàn)的,它可以同時監(jiān)控多個文件描述符,當(dāng)某個文件描述符就緒時,會通知對應(yīng)程序進行讀/寫操作。
epoll 主要有三個方法,分別是 epoll_create
、epoll_ctl
、epoll_wait
。
int epoll_create(int size)
其功能主要是創(chuàng)建一個 epoll
句柄并返回,傳入的 size 代表監(jiān)聽的描述符個數(shù)(僅僅是初次分配的 fd 個數(shù))
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
其功能是對 epoll 事件進行注冊,會對該 fd 執(zhí)行指定的 op 操作,參數(shù)含義如下:
op:對 fd 執(zhí)行的操作
epoll_event 是一個結(jié)構(gòu)體,里面的 events 代表了對應(yīng)文件操作符的操作,而 data 代表了用戶可用的數(shù)據(jù)。
其中 events 可取下面幾個值:
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
其功能是等待事件的上報,參數(shù)含義如下:
當(dāng)調(diào)用了該方法后,會進入阻塞狀態(tài),等待 epfd 上的 IO 事件,若 epfd 監(jiān)聽的某個文件描述符發(fā)生前面指定的 event 時,就會進行回調(diào),從而使得 epoll 被喚醒并返回需要處理的事件個數(shù)。若超過了設(shè)定的超時時間,同樣也會被喚醒并返回 0 避免一直阻塞。
而 Handler 的阻塞喚醒機制就是基于上面的 epoll
的阻塞特性,我們來看看它的具體實現(xiàn)。
在 Java 中的 MessageQueue
創(chuàng)建時會調(diào)用到 nativeInit
方法,在 native 層會創(chuàng)建 NativeMessageQueue
并返回其地址,之后都是通過這個地址來與該 NativeMessageQueue
進行通信(也就是 MessageQueue
中的 mPtr
,類似 MMKV
的做法),而在 NativeMessageQueue
創(chuàng)建時又會創(chuàng)建 Native 層下的 Looper
,我們看到 Native 下的 Looper
的構(gòu)造函數(shù):
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK); //構(gòu)造喚醒事件的fd
AutoMutex _l(mLock);
rebuildEpollLocked();
}
可以看到,它調(diào)用了 rebuildEpollLocked
方法對 epoll
進行初始化,讓我們看看其實現(xiàn)
void Looper::rebuildEpollLocked() {
if (mEpollFd >= 0) {
close(mEpollFd);
}
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event));
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
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);
}
}
可以看到,這里首先關(guān)閉了舊的 epoll
描述符,之后又調(diào)用了 epoll_create
創(chuàng)建了新的 epoll
描述符,然后進行了一些初始化后,將 mWakeEventFd
及 mRequests
中的 fd
都注冊到了 epoll
的描述符中,注冊的事件都是 EPOLLIN
。
這就意味著當(dāng)這些文件描述符其中一個發(fā)生了 IO 時,就會通知 epoll_wait
使其喚醒,那么我們猜測 Handler 的阻塞就是通過 epoll_wait
實現(xiàn)的。
同時可以發(fā)現(xiàn),Native 層也是存在 MessageQueue
及 Looper
的,也就是說 ative
層實際上也是有一套消息機制的,這些我們到后面再進行介紹。
我們看看阻塞,它的實現(xiàn)就在我們之前看到的 MessageQueue::next
中,當(dāng)發(fā)現(xiàn)要返回的消息將來才會執(zhí)行,則會計算出當(dāng)下距離其將要執(zhí)行的時間還差多少毫秒,并調(diào)用 nativePollOnce
方法將返回的過程阻塞到指定的時間。
nativePollOnce
很顯然是一個 native 方法,它最后調(diào)用到了 Looper
這個 native 層類的 pollOnce
方法。
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
if (result != 0) {
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
result = pollInner(timeoutMillis);
}
}
前面主要是一些對 Native 層消息機制的處理,我們先暫時不關(guān)心,這里最后調(diào)用到了 pollInner
方法:
int Looper::pollInner(int timeoutMillis) {
// ...
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// 1
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// ...
return result;
}
可以發(fā)現(xiàn),這里在 1 處調(diào)用了 epoll_wait
方法,并傳入了我們之前在 natviePollOnce
方法傳入的當(dāng)前時間距下個任務(wù)執(zhí)行時間的差值。這就是我們的阻塞功能的核心實現(xiàn)了,調(diào)用該方法后,會一直阻塞,直到到達我們設(shè)定的時間或之前我們在 epoll
的 fd
中注冊的幾個 fd
發(fā)生了 IO。其實到了這里我們就可以猜到,nativeWake
方法就是通過對注冊的 mWakeEventFd
進行操作從而實現(xiàn)的喚醒。
后面主要是一些對 Native 層消息機制的處理,這篇文章暫時不關(guān)注,它的邏輯和 Java 層是基本一致的。
nativeWake
方法最后通過 NativeMessageQueue
的 wake 方法調(diào)用到了 Native 下 Looper
的 wake 方法:
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
這里其實就是調(diào)用了 write 方法,對 mWakeEventFd
中寫入了 1,從而使得監(jiān)聽該fd
的 pollOnce
方法被喚醒,從而使得 Java 中的 next 方法繼續(xù)執(zhí)行。
那我們再回去看看,在什么情況下,Java 層會調(diào)用 natvieWake
方法進行喚醒呢?
MessageQueue
類中調(diào)用 nativeWake
方法主要有下列幾個時機:
MessageQueue
的 quit 方法進行退出時,會進行喚醒可以發(fā)現(xiàn),主要是在可能不再需要阻塞的情況下進行喚醒。(比如加入了一個更早的任務(wù),那繼續(xù)阻塞顯然會影響這個任務(wù)的執(zhí)行)
Android 的消息機制在 Java 層及 Native 層均是由 Handler
、Looper
、MessageQueue
三者構(gòu)成
async
,默認為 "默認為 true。若 async
為 false 則該 Handler 發(fā)送的 Message 均為異步消息,有同步屏障的情況下會被優(yōu)先處理"MessageQueue
的類,每個線程有一個獨有的 Looper
,它會在所處的線程開啟一個死循環(huán),不斷從 MessageQueue
中拿出消息,并將其發(fā)送給 target 進行處理nativePollOnce
進入阻塞狀態(tài),避免資源的浪費。若存在消息屏障,則會忽略同步消息優(yōu)先拿取異步消息,從而實現(xiàn)異步消息的優(yōu)先消費。
下面還有一些與 Handler 相關(guān)的常見問題,可以結(jié)合前面的內(nèi)容得到答案。
Looper
是在主線程創(chuàng)建,同時其 loop 方法也是在主線程執(zhí)行,為什么這樣一個死循環(huán)卻不會阻塞主線程呢?
我們看到 ActivityThread
中,它實際上是有一個 handleMessage
方法,其實 ActivityThread
就是一個 Handler,我們在使用的過程中的很多事件(如 Activity、Service 的各種生命周期)都在這里的各種 Case 中,也就是說我們平時說的主線程其實就是依靠這個 Looper
的 loop 方法來處理各種消息,從而實現(xiàn)如 Activity 的聲明周期的回調(diào)等等的處理,從而回調(diào)給我們使用者。
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case XXX:
// ...
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
}
因此不能說主線程不會阻塞,因為主線程本身就是阻塞的,其中所有事件都由主線程進行處理,從而使得我們能在這個循環(huán)的過程中作出自己的各種處理(如 View 的繪制等)。
而這個問題的意思應(yīng)該是為何這樣一個死循環(huán)不會使得界面卡頓,這有兩個原因:
Handler 的內(nèi)存泄漏是怎么回事?如何產(chǎn)生的呢?
首先,造成 Handler 的內(nèi)存泄漏往往是因為如下的這種代碼:
public class XXXActivity extends BaseActivity {
// ...
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 一些處理
}
};
// ...
}
那這樣為什么會造成內(nèi)存泄漏呢?
我們都知道,匿名內(nèi)部類會持有外部類的引用,也就是說這里的 Handler 會持有其外部類 XXXActivity
的引用。而我們可以回憶一下 sendMessage
的過程中,它會將 Message 的 target 設(shè)置為 Handler,也就是說明這個 Message 持有了 mHandler
的引用。那么我們假設(shè)通過 mHandler
發(fā)送了一個 2 分鐘后的延時消息,在兩分鐘還沒到的時候,我們關(guān)閉了界面。按道理來說此時 Activity 可以被 GC 回收,但由于此時 Message 還處于 MessageQueue
中,MessageQueue
這個對象持有了 Message 的引用,Message 又持有了我們的 Handler 的引用,同時由于 Handler 又持有了其外部類 XXXActivity
的引用。這就導(dǎo)致此時 XXXActivity
仍然是可達的,因此導(dǎo)致 XXXActivity
無法被 GC
回收,這就造成了內(nèi)存泄漏。
因此我們使用 Handler 最好將其定義為 static 的,避免其持有外部類的引用導(dǎo)致類似的內(nèi)存泄漏問題。如果此時還需要用到 XXXActivity
的一些信息,可以通過 WeakReference
來使其持有 Activity 的弱引用,從而可以訪問其中的某些信息,又避免了內(nèi)存泄漏問題。
(更多完整項目下載。未完待續(xù)。源碼。圖文知識后續(xù)上傳github。)
可以點擊關(guān)于我聯(lián)系我獲取完整PDF
(VX:mm14525201314)
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。