/**
* {@hide}
*
* Not available for general use. If you need help, hang up and then dial one of the following
*/
//这个是Android 30的代码,这里标记在Android 28以上这个属性不允许外部应用访问了,而是包装了一系列接口来访问AttachInfo中的内容,不过对我们的分析没什么影响
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
AttachInfo mAttachInfo;
/**
* <p>Causes the Runnable to be added to the message queue.
* The runnable will be run on the user interface thread.</p>
*/
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;
}
首先看mAttachInfo
是在哪里被赋值的
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
...
}
如果mAttachInfo
不为空,则调用mAttachInfo
的mHandler
来处理消息。AttachInfo
为View.java
的一个静态内部类,它的mHandler
是在构造方法中被初始化的,而构造方法是在ViewRootImpl
构造方法中调用的
public ViewRootImpl(Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
}
final ViewRootHandler mHandler = new ViewRootHandler();
可以知道,在mAttachInfo
在初始化之后,view.post()
直接将消息交给ViewRootImpl
的mHandler
处理。
暂时不知道dispatchAttachedToWindow
的调用时机,先看一下如果mAttachInfo
为空怎么进行,也就是看一下getRunQueue().post(action);
View.java
/**
* Returns the queue of runnable for this view.
*
* @return the queue of runnables for this view
*/
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
所以调用的是HandlerActionQueue
的post()
public void post(Runnable action) {
postDelayed(action, 0);
}
public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}
//包装了传入的Runnable和延迟时间
private static class HandlerAction {
final Runnable action;
final long delay;
public HandlerAction(Runnable action, long delay) {
this.action = action;
this.delay = delay;
}
//看起来是比较两个Runnable,暂时不知道干嘛
public boolean matches(Runnable otherAction) {
return otherAction == null && action == null
|| action != null && action.equals(otherAction);
}
}
看到这里发现其实并没有执行传入的Runnable
,总结一下这部分操作,在mAttachInfo
没有初始化好的时候,在view
对象中会搞出【哦这窒息的用词>.<】一个队列保存这些Runnable
。
所以下一步只能继续跟踪mAttachInfo
初始化从而找出这些Runnable在哪里执行,继续回到dispatchAttachedToWindow
方法,找到了执行队列中保存的Runnable的地方
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
...
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
}
可以看出仍然是交给了mAttachInfo
中的mHandler
来处理,和前面分析的一样,还是交给了ViewRootImpl
来处理。继续找dispatchAttachedToWindow
的调用:
ViewRootImpl.java
@UnsupportedAppUsage
View mView;
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
...
host.dispatchAttachedToWindow(mAttachInfo, 0);
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw();
}
ViewRootImpl
中的mView
指的是该ViewRootImpl
指依赖的Activity
中的DecorView
,此为场外信息,这里不作分析~从上面可以看出,是在View
的绘制流程之前调用了dispatchAttachedToWindow
,从前面的分析知道,dispatchAttachedToWindow
是把Runnable
交给了ViewRootImpl
中的mHandler
来处理,而这个mHandler
绑定的是主线程的Looper
,在这里发送消息后会将消息放在主线程消息队列的尾部。performTraversals
以及当前的绘制流程是当前主线程正在执行的消息,所以view.post
发出的消息一定会在这个view
绘制结束之后执行。