Android View.post()

View.post()方法使用场景

  1. 子线程中更新ui。
  2. onCreate()中调用获取view宽高。

下面看看源码

public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        getRunQueue().post(action);
        return true;
    }

这里有两个流程:

  • AttachInfo不为null时,attachInfo.mHandler.post(action)->Handler.post()
  • AttachInfo为null时调用getRunQueue().post(action)
private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }

HandlerActionQueue.post()

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和延迟时间封装到HandlerAction

private static class HandlerAction {
        final Runnable action;
        final long delay;

        public HandlerAction(Runnable action, long delay) {
            this.action = action;
            this.delay = delay;
        }

        public boolean matches(Runnable otherAction) {
            return otherAction == null && action == null
                    || action != null && action.equals(otherAction);
        }
    }

GrowingArrayUtils.append(mActions, mCount, handlerAction)。保存handlerAction到数组mActions,mActions是HandlerActionQueue的成员变量。

HandlerActionQueue.executeActions()很明显操作了HandlerAction。

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.postDelayed()

当然这解释不了最开始的问题:

  • View.post()为何能在子线程中更新ui。
  • View.post()为何能在onCreate()中获取宽高。

下面看executeActions()调用处。

View.dispatchAttachedToWindow()

if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }

ViewRootImpl.performTraversals()

final View host = mView;
...
host.dispatchAttachedToWindow(mAttachInfo, 0);
...
getRunQueue().executeActions(mAttachInfo.mHandler)
...
//绘制流程
performMeasure(...);
performLayout(...);
performDraw();

performTraversals()是ui绘制入口,这里调用View.dispatchAttachedToWindow()传入了AttachInfo。所以AttachInfo不为null意味着view树已经绘制完毕,前面attachInfo.mHandler.post(action)自然也就能获取view的宽高。

但是这里有个问题:dispatchAttachedToWindow()明显在绘制之前调用,View.post()如何保证在绘制后执行?performTraversals()本身也是运行在主线程Looper事件循环中,executeActions()调用handler.post()发送消息执行传人的Runnable,事件队列会先执行完performTraversals()才执行下一条消息。

mAttachInfo在ViewRootImpl构造方法中初始化

mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                context);

这里先不说ViewRootImpl,只需知此处mHandler是主线程中初始化的,所以子线程中View.post()可以操作ui。

总结:view树还未绘制完毕,View.post()传入的Runnable会保存在HandlerAction数组中。待view树绘制完毕,ViewRootImpl调用view.dispatchAttachedToWindow()->HandlerActionQueue.executeActions()->Handler.post()发送消息,执行Runnable;view树绘制完毕,直接执行Handler.post()。

你可能感兴趣的:(Android View.post())