View#post(Runnable)的执行流程

  • View.post(Runnable) 为什么可以得到 View 的真实宽高
  • Handler.post(Runnable)View.post(Runnable)有什么区别
  • onCreateonResume 函数中为什么无法直接得到 View 的真实宽高
  • View.post(Runnable) 中的 Runnable 是由谁来执行的,可以保证一定会被执行吗

View.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;
    }

AttachInfo 是 View 的静态内部类,每个View都会持有一个AttachInfo ,它默认为null;

AttachInfo赋值

View中能给mAttachInfo赋值的地方只有一处,在 dispatchAttachedToWindow()方法里赋值

void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
        //省略。。。
}

 初始化AttachInfo

ViewRootImpl#performTraversals中执行dispatchAttachedToWindow且只执行一次

 private void performTraversals() {
     // cache mView since it is used so much below...
     final View host = mView;
    //省略。。。
    if (mFirst) {
        //省略。。。
        host.dispatchAttachedToWindow(mAttachInfo, 0);
        mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
        dispatchApplyInsets(host);
        //Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
    }
    // Execute enqueued actions on every traversal in case a detached view enqueued an action
    getRunQueue().executeActions(mAttachInfo.mHandler);
    ...
        performMeasure();
    ...
        performLayout();
    ...
        performDraw();
}

执行顺序 

虽然在程序执行顺序上getRunQueue().executeActions(mAttachInfo.mHandler)先添加任务到消息队列,然后执行View测量performMeasure();然而Android 系统是由事件驱动的,故需要将当前任务执行完成之后,再次轮询消息队列中的其他任务。所以可以测量出View的宽高

可以理解把任务提交给Looper轮询中-但自己本身正在执行-需要执行完成再次启动下一次轮询

当前AttachInfo 为空是需查看#HandlerActionQueue #本地存储队列/执行任务

    private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }

    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++;
        }
    }

ViewRootImpl.getRunQueue().post(action);#7.0之前

getRunQueue().post(action);//7.0之后

#每个线程存在一个#7.0之前

static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();

#开启执行队列#故子线程和主线程执行post获取的不是同一个队列RunQueue getRunQueue().executeActions(mAttachInfo.mHandler);

不是不可以在子线程中调用View.post,要在View.onAttachToWindow之后在执行;

每个View维护一个队列#7.0之后

private HandlerActionQueue mRunQueue; getRunQueue().executeActions(mAttachInfo.mHandler);

在老API中任务队列存储在#每一个线程存在#ThreadLocal

    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);         ①
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);        ②
        return true;
    }

    static RunQueue getRunQueue() {
        RunQueue rq = sRunQueues.get();
        if (rq != null) {
            return rq;
        }
        rq = new RunQueue();
        sRunQueues.set(rq);
        return rq;
    }

所以在子线程中使用View.post()且在AttachInfo未注册时# getRunQueue时在主线程中调用主线程的ThreadLocal#获取不到子线程存储的ThreadLocal#则存在子线程任务未执行的风险

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值