解析 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 再拿宽高信息。
流程图归纳如下
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于