【Android】使用View.post获取宽高

为什么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交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值