view.post源码解析 (API29)


View.post(runnable) runnable在主线程执行,且能拿到View的宽高等测量绘制信息

view.post投递消息
  • 如果attachInfo不为空,则直接由attachInfo的handler来执行runnable 信息
  • 否则,放入HandlerActionQueue中,等待attachInfo赋值,然后由attachInfo.handler执行runnable

综上,view.post(runnable)都是通过attachInfo.handler发送消息,由handler分发处理。
attachInfo是在dispatchAttachedToWindow中赋值,在dispatchDetachedFromWindow中移除的。

//View.java
	//投递消息
	public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) { //attachInfo已经赋值,则直接通过attachInfo.handler执行
            return attachInfo.mHandler.post(action);
        }
        getRunQueue().post(action); //否则放入HandlerActionQueue中,等待时机处理
        return true;
    }

	//取消执行runnable对象,和post对应
	public boolean removeCallbacks(Runnable action) {
        if (action != null) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null) {
                attachInfo.mHandler.removeCallbacks(action);
                attachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
                        Choreographer.CALLBACK_ANIMATION, action, null);
            }
            getRunQueue().removeCallbacks(action);
        }
        return true;
    }
    
	AttachInfo mAttachInfo;
    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;

		// Transfer all pending runnables.
        if (mRunQueue != null) { //attachInfo.handler执行之前存储的runnable信息
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
    }

    void dispatchDetachedFromWindow() {  
        mAttachInfo = null;
    }

private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }
HandlerActionQueue管理attachInfo未实例化之前的runnable对象
  • 将runnable 和 delayTime 包装成一个HandlerAction对象
  • 存储管理HandlerAction对象,然后通过传来的handler发消息执行runnable对象
public class HandlerActionQueue {
    private HandlerAction[] mActions;
    private int mCount; //集合容量

    public void post(Runnable action) {
        postDelayed(action, 0);
    }

    public void postDelayed(Runnable action, long delayMillis) {
    //将runnable 和 delayTime 包装成一个HandlerAction对象
        final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
        synchronized (this) {
            if (mActions == null) { //默认初始化数组对象容量为4,
                mActions = new HandlerAction[4];
            }
            mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
            mCount++;
        }
    }

	//更新集合数据mActions
    public void removeCallbacks(Runnable action) {
        synchronized (this) {
            final int count = mCount;
            int j = 0; //j是新的,处理过的的Hnadler集合索引
            final HandlerAction[] actions = mActions;
            for (int i = 0; i < count; i++) { //i是实际的Hnadler集合索引
                if (actions[i].matches(action)) {
                    continue;
                }
                if (j != i) { //不匹配,用后面的覆盖前面的(j肯定小于等于i)
                    actions[j] = actions[i];
                }
                j++;
            }
            mCount = j; //集合的容量更新
            for (; j < count; j++) { //集合被清除的数据赋为null
                actions[j] = null;
            }
        }
    }

	//通过handler发送message的形式,在handler所在的线程执行所有action
    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;
        }
    }

    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);
        }
    }
}
attachInfo的实例化和赋值
  • attachInfo是在ViewRootImpl的构造函数中实例化的,mHandler的looper是当前线程(主线程)的looper对象,handler也是在主线程中处理消息的

  • ViewRootImple是在onResume之后,在WindowManagerGlobal中实例化的,并通过setView(view, display)绑定了decorView,viewRootImpl是decorView的parentView

  • setView中通过requestLayout发起布局绘制请求,在下一个16.66ms到来时,执行performTraversals()。

  • 在performMeasure之前,通过host.dispatchAttachedToWindow(attachInfo),遍历decorView,将attachInfo绑定到每一个View/ViewGroup身上,才能通过attachInfo.handler发消息执行所有runnable对象

  • runnable对象是在performTraversals()执行完毕后,handler才会继续从messageQueue中取message,在主线程中执行,所以view.post(runnable)能拿到View的宽高等测量绘制信息。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {

final View.AttachInfo mAttachInfo;
final ViewRootHandler mHandler = new ViewRootHandler();

//ViewRootImpl实例化时,attachInfo也实例化
public ViewRootImpl(Context context, Display display) {
        mFirst = true; // true for the first time the view is added
        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context);
    }

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout(); //发起布局绘制请求
				view.assignParent(this);
        }
    }

 private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
        if (host == null || !mAdded)
            return;
        if (mFirst) {
            host.dispatchAttachedToWindow(mAttachInfo, 0); //分发attachInfo给每一个view、ViewGroup
        } 
            int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
            int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
            // Ask host how big it wants to be
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            performLayout(lp, mWidth, mHeight);
            performDraw();

    }
}

decorView分发attachInfo给每一个View/ViewGroup

//ViewGroup.java
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        super.dispatchAttachedToWindow(info, visibility);
      
        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
            final View child = children[i];
            child.dispatchAttachedToWindow(info,
                    combineVisibility(visibility, child.getVisibility()));
        }
    }
总结:
  1. View.post(Runnable) 内部会自动分两种情况处理,当 View 还没 attachedToWindow 时,会先将这些 Runnable 操作缓存下来;否则就直接通过 mAttachInfo.mHandler 将这些 Runnable 操作 post 到主线程的 MessageQueue 中等待执行。
  2. 如果 View.post(Runnable) 的 Runnable 操作被缓存下来了,那么这些操作将会在 dispatchAttachedToWindow() 被回调时,通过 mAttachInfo.mHandler.post() 发送到主线程的 MessageQueue 中等待执行。
  3. mAttachInfo 是 ViewRootImpl 的成员变量,在构造函数中初始化,Activity View 树里所有的子 View 中的 mAttachInfo 都是 ViewRootImpl.mAttachInfo 的引用。
  4. mAttachInfo.mHandler 也是 ViewRootImpl 中的成员变量,在声明时就初始化了,所以这个 mHandler 绑定的是主线程的 Looper,所以 View.post() 的操作都会发送到主线程中执行,那么也就支持 UI 操作了。
  5. dispatchAttachedToWindow() 被调用的时机是在 ViewRootImol 的 performTraversals() 中,该方法会进行 View 树的测量、布局、绘制三大流程的操作。
  6. Handler 消息机制通常情况下是一个 Message 执行完后才去取下一个 Message 来执行(异步 Message 还没接触),所以 View.post(Runnable) 中的 Runnable 操作肯定会在 performMeaure() 之后才执行,所以此时可以获取到 View 的宽高。

参考:
【Android源码解析】View.post()到底干了啥

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值