十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。

10余年的西吉网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。成都全网营销推广的优势是能够根据用户设备显示端的尺寸不同,自动调整西吉建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。创新互联从事“西吉网站设计”,“西吉网站推广”以来,每个客户项目都认真落实执行。
为什么 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;
 - }
 
2、getRunQueue().post()
看下 getRunQueue().post():
- private HandlerActionQueue getRunQueue() {
 - if (mRunQueue == null) {
 - mRunQueue = new HandlerActionQueue();
 - }
 - return mRunQueue;
 - }
 
- 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 表示一个待执行的任务,内部持有要执行的 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() 添加的任务;
梳理总结:
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
 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;
 - }
 - }
 
- // View 绘制流程开始在 ViewRootImpl
 - private void performTraversals() {
 - // mView是DecorView
 - final View host = mView;
 - if (mFirst) {
 - .....
 - // host为DecorView
 - // 调用DecorVIew 的 dispatchAttachedToWindow,并且把 mAttachInfo 给子view
 - host.dispatchAttachedToWindow(mAttachInfo, 0);
 - mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
 - dispatchApplyInsets(host);
 - .....
 - }
 - mFirst=false
 - getRunQueue().executeActions(mAttachInfo.mHandler);
 - // View 绘制流程的测量阶段
 - performMeasure();
 - // View 绘制流程的布局阶段
 - performLayout();
 - // View 绘制流程的绘制阶段
 - performDraw();
 - }
 
- void dispatchAttachedToWindow(AttachInfo info, int visibility) {
 - mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
 - super.dispatchAttachedToWindow(info, visibility);
 - mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
 - // 子View的数量
 - final int count = mChildrenCount;
 - final View[] children = mChildren;
 - // 遍历所有子View
 - for (int i = 0; i < count; i++) {
 - final View child = children[i];
 - // 遍历调用所有子View的dispatchAttachedToWindow
 - // 为每个子View关联AttachInfo
 - child.dispatchAttachedToWindow(info,
 - combineVisibility(visibility, child.getVisibility()));
 - }
 - }
 
View.post(Runnable) 内部会自动分两种情况处理,当 View 还没 attachedToWindow 时,会先将这些 Runnable 操作缓存下来;否则就直接通过 mAttachInfo.mHandler 将这些 Runnable 操作 post 到主线程的 MessageQueue 中等待执行;
View.post() 任务能够保证在所有 View 绘制流程结束之后被调用,故如果需要依赖 View 绘制任务,此时可以优先考虑使用该机制;