android原始碼解析--MessageQueue
上午,剛剛粗略的看完了handler的原始碼,與其相關類looper類和MessageQueue類也一併看下。
先來看Messagequeue,首先是類介紹:
Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through MessageQueue.IdleHandler objects associated with the Looper.
You can retrieve the MessageQueue for the current thread with Looper.myQueue().
儲存訊息列表的低階別類,訊息由Looper物件派發。訊息並不是直接新增到MessageQueue中的,而是通過與Looper物件關聯的MessageQueue.IdleHandler物件新增。
呼叫Looper.myQueue方法可以獲取當前執行緒的MessageQueue。
再來看一下類中的變數:
Message mMessages;
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuiting;
boolean mQuitAllowed = true;
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
private boolean mBlocked;
@SuppressWarnings("unused")
private int mPtr; // used by native code
private native void nativeInit();
private native void nativeDestroy();
private native void nativePollOnce(int ptr, int timeoutMillis);
private native void nativeWake(int ptr);
- Message的例項
- 存放IdleHandler物件的一個ArrayList
- 一個IdleHandler陣列
- 一個boolean值,判斷Thread是否退出
- 一個boolean值,判斷是否允許退出
- 一個boolean值,判斷next()是否阻塞等待一個非零超時的pollOnce()
- 下面的就是native code了。本文不做詳解。
前面提到Message不是直接新增到MessageQueue裡的,是通過IdleHandler介面進行Message新增,所以先看下IdleHandler:
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
*/
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
一個用於在一個執行緒為了等待更多訊息阻塞時的介面。當訊息佇列為空,等待更多訊息時,執行這個queueIdle函式,如果返回真,繼續保持你的idlehandler的活躍狀態,如果返回值為false,那麼就會從應用程式中移除這個IdleHandler,否則的話就會在應用程式中繼續維護著這個IdleHandler,下次空閒時仍會再執會這個IdleHandler。但是如果訊息佇列裡面有訊息,但是他們的執行時間在當前時間之後,queueIdle也會被呼叫。
再往下看程式碼:
/**
* Add a new {@link IdleHandler} to this message queue. This may be
* removed automatically for you by returning false from
* {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
* invoked, or explicitly removing it with {@link #removeIdleHandler}.
*
* <p>This method is safe to call from any thread.
*
* @param handler The IdleHandler to be added.
*/
public final void addIdleHandler(IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
向訊息佇列中新增一個新的MessageQueue.IdleHandler。當呼叫IdleHandler.queueIdle()返回false時,此MessageQueue.IdleHandler會自動的從訊息佇列中移除。或者呼叫removeIdleHandler(MessageQueue.IdleHandler)也可以從訊息佇列中移除MessageQueue.IdleHandler。這個方法是執行緒安全的。
有新增,就有刪除:
/**
* Remove an {@link IdleHandler} from the queue that was previously added
* with {@link #addIdleHandler}. If the given object is not currently
* in the idle list, nothing is done.
*
* @param handler The IdleHandler to be removed.
*/
public final void removeIdleHandler(IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
從佇列中移除之前呼叫addIdlehandler(MessageQueue.IdleHandler)新增的MessageQueue.IdleHandler。如果handler不在當前的空閒列表,不做任何事。
看一下它的構造方法:
MessageQueue() {
nativeInit();
}
呼叫native程式碼,完成初始化。
重寫了類的銷燬:
@Override
protected void finalize() throws Throwable {
try {
nativeDestroy();
} finally {
super.finalize();
}
}
也是呼叫native程式碼,完成類的銷燬。
下面就是獲取下一個Message方法:
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
} else {
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
// If first time, then get the number of idlers to run.
if (pendingIdleHandlerCount < 0) {
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);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
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("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
它在取Message來進行處理時通過判斷MessageQueue裡面的Message是否符合時間要求來決定是否需要把Message取出來做處理,通過這種方式做到訊息的定時處理。所以呼叫這個函式的時候,有可能會讓執行緒進入等待狀態。什麼情況下,執行緒會進入等待狀態呢?兩種情況,一是當訊息佇列中沒有訊息時,它會使執行緒進入等待狀態;二是訊息佇列中有訊息,但是訊息指定了執行的時間,而現在還沒有到這個時間,執行緒也會進入等待狀態。訊息佇列中的訊息是按時間先後來排序的。先看一下兩個引數的意思:pendingIdleHandlerCount:空閒的handler個數。只有在第一次迴圈的時候值為-1。nextPollTimeoutMillis:字面理解下次輪詢時間,如果當前訊息佇列中沒有訊息,它要等待的時候,for迴圈開始時,傳入的值為0,表示不等待。
在for迴圈內部:
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
如果nextPollTimeoutMillis值不為0,則等待訊息。然後執行下面語句是看看當前訊息佇列中有沒有訊息:
nativePollOnce(mPtr, nextPollTimeoutMillis);
nativePollOnce返回後,next函式將從mMessages中提取一個訊息。也就是說,要讓nativePollOnce返回,至少要新增一個訊息到訊息佇列,否則nativePollOnce不過是做了一次無用功罷了。
如果nativePollOnce在Native層等待,就表明Native層也可以投遞Message(訊息,為了適應業界的習慣本書沿用英文,必要時才用中文,其他詞同此),但是從Message類的實現程式碼上看,該類和Native層沒有建立任何關係(即Native層不太可能去構造Java層的Message物件並把它插入到Java層的Message佇列中)。那麼nativePollOnce在等待什麼呢?
對於上面的問題,相信有些讀者心中已有了答案:nativePollOnce除了等待Java層來的Message,還在Native層做了大量的工作。
然後再看synchronized內部程式碼:
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
} else {
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
// If first time, then get the number of idlers to run.
if (pendingIdleHandlerCount < 0) {
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);
}
先嚐試檢索出下一個message,如果存在就返回。如果message為空,nextPollTimeoutMillis就賦值為-1.就必須等待下一次訊息。如果不為空,就判斷時間是否可以處理這個訊息,如果符合條件,把訊息傳給looper處理。否則,算出需要等待時間,等待到該時間,然後執行
// If first time, then get the number of idlers to run.
if (pendingIdleHandlerCount < 0) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount == 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
如果是第一次,得到閒置的idler handler去執行。如果沒有閒置的idlehandler,阻塞,然後繼續迴圈等待更多訊息。
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
把註冊在mIdleHandlers中的IdleHandler取出來,放在mPendingIdleHandlers陣列中去。 接下來就是執行這些註冊了的IdleHanlder了:
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
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("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
執行空閒處理器。我們只能達到這個程式碼塊在第一次迭代。執行完這些IdleHandler之後,執行緒下次呼叫nativePollOnce函式時,就不設定超時時間了,因為,很有可能在執行IdleHandler的時候,已經有新的訊息加入到訊息佇列中去了,因此,要重置nextPollTimeoutMillis的值:
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
再接下來就是插入操作:
final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
final boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
如果msg正在被使用,或者msg對應的handler為空或者不允許退出,均報異常。if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
是否正在退出,如果退出,返回false,如果msg對應的handler為空,把正在退出設為true。
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
如果p為空,或者when=0或者插入時間小於p.when時,就會把傳入的訊息插入到佇列頭(這也就解釋了上篇文章中提到的問題,when=0時,插入佇列頭部),否則,插入隊尾。
接下來是刪除。
final boolean removeMessages(Handler h, int what, Object object,
boolean doRemove) {
synchronized (this) {
Message p = mMessages;
boolean found = false;
// Remove all messages at front.
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
if (!doRemove) return true;
found = true;
Message n = p.next;
mMessages = n;
p.recycle();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
if (!doRemove) return true;
found = true;
Message nn = n.next;
n.recycle();
p.next = nn;
continue;
}
}
p = n;
}
return found;
}
}
final void removeMessages(Handler h, Runnable r, Object object) {
if (r == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.callback == r
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycle();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycle();
p.next = nn;
continue;
}
}
p = n;
}
}
}
最後一個刪除:final void removeCallbacksAndMessages(Handler h, Object object) {
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycle();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycle();
p.next = nn;
continue;
}
}
p = n;
}
}
}
檢視更多原始碼內容:Android原始碼解析!
相關文章
- 深入原始碼解析Android中的Handler,Message,MessageQueue,Looper原始碼AndroidOOP
- Android多執行緒之Handler、Looper與MessageQueue原始碼解析Android執行緒OOP原始碼
- Android Handler 原始碼解析Android原始碼
- Android Retrofit原始碼解析Android原始碼
- Android——LruCache原始碼解析Android原始碼
- android LruCache原始碼解析Android原始碼
- Android EventBus原始碼解析Android原始碼
- android原始碼解析--switchAndroid原始碼
- Android原始碼解析--LooperAndroid原始碼OOP
- Android 開源專案原始碼解析 -->PhotoView 原始碼解析(七)Android原始碼View
- Android 原始碼分析之 EventBus 的原始碼解析Android原始碼
- Android原始碼解析-LiveDataAndroid原始碼LiveData
- [Android] Retrofit原始碼:流程解析Android原始碼
- Android 8.1 Handler 原始碼解析Android原始碼
- Android LayoutInflater 原始碼解析Android原始碼
- WebRTC-Android原始碼解析WebAndroid原始碼
- android原始碼解析--DialogAndroid原始碼
- android原始碼解析--MessageAndroid原始碼
- Android fragment原始碼全解析AndroidFragment原始碼
- android原始碼解析--ListView(上)Android原始碼View
- Android 開源專案原始碼解析 -->Volley 原始碼解析(十五)Android原始碼
- Android 開源專案原始碼解析 -->Dagger 原始碼解析(十三)Android原始碼
- Android 開源專案原始碼解析 -->CircularFloatingActionMenu 原始碼解析(八)Android原始碼
- Android LayoutInflater Factory 原始碼解析Android原始碼
- Android setContentView原始碼解析AndroidView原始碼
- Android Volley框架原始碼解析Android框架原始碼
- Android訊息機制全面解析(Handler,MessageQueue,Looper,Threadlocal)AndroidOOPthread
- Android系統原始碼目錄解析Android原始碼
- Android 網路框架 Retrofit 原始碼解析Android框架原始碼
- Android AccessibilityService機制原始碼解析Android原始碼
- weex原始碼解析(四)- android引入sdk原始碼Android
- Android原始碼解析(二)動畫篇-- ObjectAnimatorAndroid原始碼動畫Object
- Android開源庫——EventBus原始碼解析Android原始碼
- Android 原始碼解析 之 setContentViewAndroid原始碼View
- React Native 0.55.4 Android 原始碼分析(Java層原始碼解析)React NativeAndroid原始碼Java
- Android 開源專案原始碼解析 -->Android Universal Image Loader 原始碼分析(十四)Android原始碼
- Android NFC技術解析,附Demo原始碼Android原始碼
- Android View 原始碼解析(一) - setContentViewAndroidView原始碼