解析View.post方法。分析一下這個方法的流程。

說起post方法,我們很容易聯想到Handlerpost方法,都是接收一個Runnable物件。那麼這兩個方法有啥不同呢?

Handler的post方法

先來簡單看一下Handlerpost(Runnable)方法。這個方法是將一個Runnable加到訊息佇列中,並且會在這個handler關聯的執行緒裡執行。

下面是關聯的部分原始碼。可以看到傳入的Runnable物件,裝入Message後,被新增進了queue佇列中。

Handler 有關的部分原始碼

    // android.os Handler 有關的部分原始碼
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;
} public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
} 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);
}

具體流程,可以看handler介紹

View的post方法

我們直接跟著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;
} private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}

可以看到一開始就查詢是否有attachInfo,如果有,則用attachInfo.mHandler來執行這個任務。

如果沒有attachInfo,則新增到View自己的mRunQueue中。確定執行的執行緒後,再執行任務。

post(Runnable action)的返回boolean值,如果為true,表示任務被新增到訊息佇列中了。

如果是false,通常表示訊息佇列關聯的looper正在退出。

那麼我們需要了解AttachInfoHandlerActionQueue

AttachInfo

AttachInfoView的靜態內部類。View關聯到父window後,用這個類來儲存一些資訊。

AttachInfo儲存的一部分資訊如下:

  • WindowId mWindowId window的標誌
  • View mRootView 最頂部的view
  • Handler mHandler 這個handler可以用來處理任務

HandlerActionQueue

View還沒有handler的時候,拿HandlerActionQueue來快取任務。HandlerAction是它的靜態內部類,儲存Runnable與延時資訊。

public class HandlerActionQueue {
private HandlerAction[] mActions; public void post(Runnable action)
public void executeActions(Handler handler)
// ... private static class HandlerAction {
final Runnable action;
final long delay;
// ...
}
}

View的mRunQueue

將任務(runnable)排成隊。當View關聯上視窗並且有handler後,再執行這些任務。

/**
* Queue of pending runnables. Used to postpone calls to post() until this
* view is attached and has a handler.
*/
private HandlerActionQueue mRunQueue;

這個mRunQueue裡儲存的任務啥時候被執行?我們關注dispatchAttachedToWindow方法。

void dispatchAttachedToWindow(AttachInfo info, int visibility) {
// ...
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
// ...
}

這個方法裡呼叫了mRunQueue.executeActions

executeActions(Handler handler)方法實際上是用傳入的handler處理佇列中的任務。

而這個dispatchAttachedToWindow會被ViewGroup中被呼叫。

或者是ViewRootImpl中呼叫

host.dispatchAttachedToWindow(mAttachInfo, 0);

小結

View的post方法,實際上是使用了AttachInfohandler

如果View當前還沒有AttachInfo,則把任務新增到了View自己的HandlerActionQueue佇列中,然後在dispatchAttachedToWindow中把任務交給傳入的AttachInfohandler。也可以這樣認為,View.post用的就是handler.post

我們在獲取View的寬高時,會利用View的post方法,就是等View真的關聯到window再拿寬高資訊。

流程圖歸納如下

更多請參見Android合集的最近更新