View.post()在绘制流程之后执行原理详解

/**
  * {@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不为空,则调用mAttachInfomHandler来处理消息。AttachInfoView.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()直接将消息交给ViewRootImplmHandler处理。
暂时不知道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;
}

所以调用的是HandlerActionQueuepost()

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绘制结束之后执行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值