解析View.post
方法。分析一下這個方法的流程。
說起post方法,我們很容易聯想到Handler的post
方法,都是接收一個Runnable物件。那麼這兩個方法有啥不同呢?
Handler的post方法
先來簡單看一下Handler的post(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正在退出。
那麼我們需要了解AttachInfo和HandlerActionQueue。
AttachInfo
AttachInfo是View的靜態內部類。View關聯到父window後,用這個類來儲存一些資訊。
AttachInfo儲存的一部分資訊如下:
WindowId mWindowId
window的標誌View mRootView
最頂部的viewHandler 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方法,實際上是使用了AttachInfo的handler
。
如果View當前還沒有AttachInfo,則把任務新增到了View自己的HandlerActionQueue佇列中,然後在dispatchAttachedToWindow
中把任務交給傳入的AttachInfo的handler
。也可以這樣認為,View.post
用的就是handler.post
。
我們在獲取View的寬高時,會利用View的post方法,就是等View真的關聯到window再拿寬高資訊。
流程圖歸納如下
更多請參見Android合集的最近更新