Android -为什么View.post可以获取宽高

·  阅读 479
为什么View.post可以获取宽高

先说结论:利用View mAttachInfo关联的Handler往主线程发送任务,任务是在绘制任务之后执行,所以自然就能获取到View的宽高。

源码分析基于android-28

View.post发送任务
    //View类中
    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);//1
        }
    
        getRunQueue().post(action);//2
        return true;
    }
复制代码
  • 注释1:当mAttachInfo不为空,则获取Handler发送任务,mAttachInfo是View添加到Window的标志,不为空则表示添加到Window了,但是不一定绘制完成,Handler是添加View的线程,默认是主线程的;
  • 注释2:当mAttachInfo为空,往其消息队列添加任务,等待被执行,注意是等待,getRunQueue()获取是HandlerActionQueue类型对象;
HandlerActionQueue.post添加任务
 //View类中
public void post(Runnable action) {
    postDelayed(action, 0);//1
}

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);//2
        mCount++;
    }
}
复制代码
  • 注释1:post会走到postDelayed,只不过delayMillis为0;
  • 注释2:将post的任务(获取宽高),封装成HandlerAction添加到mActions数组当中;
数组任务什么时候被执行

View.dispatchAttachedToWindow是发起点

//View类中
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
     mAttachInfo = info;  //1
     ......
     // Transfer all pending runnables
     if (mRunQueue != null) {//2
         mRunQueue.executeActions(info.mHandler);
         mRunQueue = null;//3
     }
     ......
}
复制代码
  • 注释1:看到这里了吗,这里会给mAttachInfo赋值;
  • 注释2:mRunQueue跟getRunQueue()对应,当其不为空,则表示有任务需要被执行,将mAttachInfo的Handler作为参数传入,,mRunQueue是HandlerActionQueue类型
执行队列中的任务
//HandlerActionQueuel类中
public void executeActions(Handler handler) {
    synchronized (this) {
        final HandlerAction[] actions = mActions;
        for (int i = 0, count = mCount; i < count; i++) {
            final HandlerAction handlerAction = actions[i];
            handler.postDelayed(handlerAction.action, handlerAction.delay);//1
        }
        mActions = null;
        mCount = 0;
    }
}
复制代码
  • 注释1:这里没什么特别的,就是往mAttachInfo的Handler发送任务;

到这里发现,无论是mAttachInfo是否为空,最终都是往mAttachInfo的Handler发送任务;

只是发送任务,也不能保证它绘制任务之后吧?

这里涉及到View的绘制流程,我们知道绘制流程是从ViewRootImpl.performTraversals发起的,

    //ViewRootImpl类中
    private void performTraversals() {       
           final View host = mView;//1
           ......
            host.dispatchAttachedToWindow(mAttachInfo, 0);//2
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
             ......
             performMeasure();
             ......
             performLayout();
             ......
             performDraw();
             ......
        }
复制代码
  • 注释1:mView就是DecorView,这里表示从顶层View开始dispatchAttachedToWindow,
  • 注释2:mAttachInfo是在创建ViewRootImpl的时候实例化的,其维护的Handler对应主线程,有兴趣可以看到ViewRootImpl的构造函数;Android是消息驱动的,获取View宽高Runnable需要在主线程执行,绘制任务(performTraversals)也是主线程执行,而前者是在后者中发送的,所以执行顺序就很显然了,执行完绘制Runnable,再执行获取宽高Runnable,所以就能正常宽高。
延伸

除了获取宽高场景,View.post不失为一种与主线程通讯的方式。

以上分析有不对的地方,请指出,互相学习,谢谢哦!

分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改