
本文转载自微信公众号「Android开发编程」,进阶之件宽作者Android开发编程。源码转载本文请联系Android开发编程公众号。中分
为什么 View.post() 的何获操作是可以对 UI 进行操作的呢,即使是取控在子线程中调用 View.post()?
今天我们就来分析分析

1、进阶之件宽View.post()
View 的源码 post 方法如下:
public boolean post(Runnable action) { // 1、首先判断AttachInfo是中分否为null final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { // 1.1如果不为null,直接调用其内部Handler的post return attachInfo.mHandler.post(action); } // 2、否则加入当前View的何获等待队列 getRunQueue().post(action); return true; } AttachInfo 是 View 的静态内部类,每个 View 都会持有一个 AttachInfo,取控它默认为 null; 如果mAttachInfo为空,进阶之件宽就执行:把action加入当前view的源码等待队列;2、getRunQueue().post()
看下 getRunQueue().post():
private HandlerActionQueue getRunQueue() { if (mRunQueue == null) { mRunQueue = new HandlerActionQueue(); } return mRunQueue; } getRunQueue() 返回的中分是 HandlerActionQueue; 调用了 HandlerActionQueue 的 post 方法: public void post(Runnable action) { // 调用到postDelayed方法,这有点类似于Handler发送消息 postDelayed(action,何获 0); } // 实际调用postDelayed public void postDelayed(Runnable action, long delayMillis) { // HandlerAction表示要执行的任务 final HandlerAction handlerAction = new HandlerAction(action, delayMillis); synchronized (this) { if (mActions == null) { // 创建一个保存HandlerAction的数组 mActions = new HandlerAction[4]; } // 表示要执行的任务HandlerAction 保存在 mActions 数组中 mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction); // mActions数组下标位置累加1 mCount++; } }3、HandlerAction
HandlerAction 表示一个待执行的取控任务,内部持有要执行的WordPress模板 Runnable 和延迟时间;=
private static class HandlerAction { // post的任务 final Runnable action; // 延迟时间 final long delay; public HandlerAction(Runnable action, long delay) { this.action = action; this.delay = delay; } // 比较是否是同一个任务 // 用于匹配某个 Runnable 和对应的HandlerAction public boolean matches(Runnable otherAction) { return otherAction == null && action == null || action != null && action.equals(otherAction); } }postDelayed() 创建一个默认长度为 4 的 HandlerAction 数组,用于保存 post() 添加的任务;
梳理总结:
我们调用 View.post(Runnable) 传进去的 Runnable 操作,在传到 HandlerActionQueue 里会先经过 HandlerAction 包装一下,然后再缓存起来; 在 执行 View.post(Runnable) 时,因为这时候 View 还没有 attachedToWindow,所以这些 Runnable 操作其实并没有被执行,而是先通过 HandlerActionQueue 缓存起来;4、AttachInfo
看下 AttachInfo 的创建过程,先看下它的构造方法:
AttachInfo(IWindowSession session, IWindow window, Display display, ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer, Context context) { mSession = session; mWindow = window; mWindowToken = window.asBinder(); mDisplay = display; // 持有当前ViewRootImpl mViewRootImpl = viewRootImpl; // 当前渲染线程Handler mHandler = handler; mRootCallbacks = effectPlayer; mTreeObserver = new ViewTreeObserver(context); }AttachInfo 中持有当前线程的 Handler;
4.1、mAttachInfo 赋值:
void dispatchAttachedToWindow(AttachInfo info, int visibility) { // 给当前View赋值AttachInfo,此时所有的View共用同一个AttachInfo(同一个ViewRootImpl内) mAttachInfo = info; if (mOverlay != null) { // 任何一个View都有一个ViewOverlay // ViewGroup的是ViewGroupOverlay // 它区别于直接在类似RelativeLaout/FrameLayout添加View,通过ViewOverlay添加的元素没有任何事件 // 此时主要分发给这些View浮层 mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility); } mWindowAttachCount++; if ((mPrivateFlags & PFLAG_SCROLL_CONTAINER) != 0) { mAttachInfo.mScrollContainers.add(this); mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED; } // mRunQueue,就是在前面的 getRunQueue().post() // 实际类型是 HandlerActionQueue,内部保存了当前View.post的任务 if (mRunQueue != null) { // 执行使用View.post的服务器托管任务 // 注意这里是post到渲染线程的Handler中 mRunQueue.executeActions(info.mHandler); // 保存延迟任务的队列被置为null,因为此时所有的View共用AttachInfo mRunQueue = null; } performCollectViewAttributes(mAttachInfo, visibility); // 回调View的onAttachedToWindow方法 // 在Activity的onResume方法中调用,但是在View绘制流程之前 onAttachedToWindow(); ListenerInfo li = mListenerInfo; final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners = li != null ? li.mOnAttachStateChangeListeners : null; if (listeners != null && listeners.size() > 0) { for (OnAttachStateChangeListener listener : listeners) { // 通知所有监听View已经onAttachToWindow的客户端,即view.addOnAttachStateChangeListener(); // 但此时View还没有开始绘制,不能正确获取测量大小或View实际大小 listener.onViewAttachedToWindow(this); } } }mRunQueue 就是保存了 View.post() 任务的 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中,等待执行 handler.postDelayed(handlerAction.action, handlerAction.delay); } //此时不在需要,后续的post,将被添加到AttachInfo中 mActions = null; mCount = 0; } } 遍历所有已保存的任务,发送到 Handler 中排队执行; 将保存任务的 mActions 置为 null,因为后续 View.post() 直接添加到 AttachInfo 内部的 Handler;View.post(Runnable) 内部会自动分两种情况处理,当 View 还没 attachedToWindow 时,会先将这些 Runnable 操作缓存下来;否则就直接通过 mAttachInfo.mHandler 将这些 Runnable 操作 post 到主线程的 MessageQueue 中等待执行;
View.post() 任务能够保证在所有 View 绘制流程结束之后被调用,故如果需要依赖 View 绘制任务,此时可以优先考虑使用该机制;
(责任编辑:域名)