美文网首页
关于view.post(r)和handler.post(r)的区

关于view.post(r)和handler.post(r)的区

作者: 最美下雨天 | 来源:发表于2019-01-29 17:07 被阅读13次

参考:
https://blog.csdn.net/Kitty_Landon/article/details/79235418
https://blog.csdn.net/scnuxisan225/article/details/49815269
其实这两个的主要区别是看目前界面有没有显示出来或者说在调用这两个方法时,view的dispatchAttachedToWindow方法有没有执行,如果已经执行过了,那么他们是没啥区别的,都是利用Handler来发送Message到MessageQueue,如果dispatchAttachedToWindow还没有执行,那么他们是有区别的,最直观的判断就是我们可以在onCreate方法中利用view.post(runnable)来获取view的宽高,但是无法利用handler.post(runnable)来获取。

为什么在onCreate方法中可以利用view.post(runnable)获取view的宽高?

看下源代码:
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为null(其实目前它就是为null的,哪儿赋值的,后面分析)

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

可以看到,在调用了view的post方法之后只是将runnable存放到了HandlerAction的数组中,并没有去执行,那么什么时候执行呢?

存放在HandlerAction数组中的runnable什么时候执行?

HandlerActionQueue#executeActions()

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

           mActions = null;
           mCount = 0;
       }
   }

在这个方法里面会调用handler来post之前存放的Runnable,看下这个方法是什么时候执行的?


image.png

dispatchAttachedToWindow是什么时候执行的呢?


image.png
我们知道ViewRootImpl的performTraversals会执行view的measure、layout、draw,大致是这样:

host.dispatchAttachedToWindow()
...
performMeasure();
...
performLayout();
...
performDraw();

理一下:系统调用ViewRootImpl#performTraversals()方法,performTraversals()方法调用host的dispatchAttachedToWindow()方法,host就是DecorView也就是View,接着在View的dispatchAttachedToWindow()方法中调用mRunQueue.executeActions()方法,这个方法内部会遍历HandlerAction数组,利用Handler来post之前存放的Runnable。

问题来了,dispatchAttachedToWindow方法是在performMeasure方法之前调用的,既然在调用的时候还没有执行performMeasure来进行测量,那么为什么在执行完dispatchAttachedToWindow方法后就可以获取到宽高呢?

因为Android系统的运行完全是基于消息驱动的。
在调用完dispatchAttachedToWindow方法之后,会将之前调用view.post(runnable)中的runnable取出来执行,这里的执行其实是发送Message到MessageQueue,等待Looper来调用执行,但是也得系统处理完上一个Message

而ViewRootImpl的performTraversals所处的环境正是一个Runnable对象,这个Runnable也是包装成Message交给Handler来处理的,所以View.post(runnable)中的runnable执行是要在performTraversals方法之后的,并非一调用dispatchAttachedToWindow就会执行。

这个流程分析完了,我们回到View的post方法中

attachInfo什么时候被赋值的呢?

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;
    }
image.png

ViewRootImpl是什么时候被初始化的呢?
在Activity的onResume()方法之后。


image.png
image.png

相关文章

网友评论

      本文标题:关于view.post(r)和handler.post(r)的区

      本文链接:https://www.haomeiwen.com/subject/vjpxsqtx.html