转载请注明链接:https://blog.csdn.net/feather_wch/article/details/79720907

以提问形式讲解View是如何在16.7ms内完成画面刷新的知识点和原理,以及相关源码的解析。

Android的刷新机制详解

版本:2018/3/27-1(18:19)

  • Android的刷新机制详解
    • 参考资料

1、界面的刷新为什么需要16.6ms?

  1. 系统每16.6ms会发出一个VSYNC信号,发出信号后,才会开始进行测量、布局和绘制。
  2. 发出VSYNC信号时,还会将此时显示器的buffer缓冲区的数据取出,并显示在屏幕上。

2、画面的显示需要哪些步骤?

  1. CPU计算数据(View树遍历并执行三大流程:测量、布局和绘制),然后将数据交给GPU“
  2. GPU渲染处理,然后将数据放到Buffer中。
  3. 显示屏(display)从buffer中取出数据,并进行显示。

3、界面保持不变时,还会16.6ms刷新一次屏幕吗?

  1. 对于底层显示器,每间隔16.6ms接收到VSYNC信号时,就会用buffer中数据进行一次显示。所以一定会刷新。

4、界面刷新的本质流程

  1. 通过ViewRootImplscheduleTraversals()进行界面的三大流程。
  2. 调用到scheduleTraversals()时不会立即执行,而是将该操作保存到待执行队列中。并给底层的刷新信号注册监听。
  3. VSYNC信号到来时,会从待执行队列中取出对应的scheduleTraversals()操作,并将其加入到主线程消息队列中
  4. 主线程消息队列中取出并执行三大流程: onMeasure()-onLayout()-onDraw()

5、View的界面刷新方法最终都会执行到 ViewRootImpl的scheduleTraversals()

  1. invalidate(请求重绘)
  2. requestLayout(重新布局)
  3. requestFocus(请求焦点)等
    界面刷新操作会从View树向上层层找到最顶层的DecorView,然后通过DecorViewmParent也就是ViewRootImpl执行scheduleTraversals()方法。

6、ViewRootImpl如何和DecorView绑定起来?

  1. Activity的启动在ActivityThread中完成,handleLaunchActivity()会依次间接执行到onCreate()-onStart()-onResume()
  2. 之后会调用WindowManageraddView()ViewWindow关联起来。
  3. addView()会创建ViewRootImpl并调用其setView(decorView),内部调用decorView.assignParent(this),将ViewRootImpl设置为DecorView的mParent

7、ViewRootImpl的scheduleTraversals()源码解析

主要分为两部分:
1. 将界面刷新操作打包后加入到待执行队列中,并监听下一次VSYNC信号。
2. 接收到VSYNC信号后,进行界面刷新-测量、布局、绘制 。

    /**=======================================================*     * 上层app请求界面刷新的主要思路:     *    1. 不会立即执行performTraversals()-测量、布局、绘制三大流程     *    2. 将performTraversals()方法封装到Runnable中,保存到“待执行队列中”     *    3. 在DisplayEventReceiver中注册监听底层的VSYNC信号     * //ViewRootImpl.java     *========================================================*/    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();    //ViewRootImpl.java    void scheduleTraversals() {        //1. mTraversalScheduled避免一帧数据内重复提交刷新请求(仅仅会在VYSNC信号后调用的doTraversal方法中置为false)        if (!mTraversalScheduled) {            mTraversalScheduled = true;            //2. 发送同步屏障            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();            //3. 本质将TraversalRunnable存放到“待执行队列中”,等待接收到VSYNC信号后取出并执行            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);            ......        }    }    //Choreographer.java    public void postCallback(int callbackType, Runnable action, Object token) {        //层层执行        postCallbackDelayed(callbackType, action, token, 0);    }    public void postCallbackDelayed(int callbackType, Runnable action, Object token, long delayMillis) {        //层层执行        postCallbackDelayedInternal(callbackType, action, token, delayMillis);    }    //Choreographer.java    private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {        synchronized (mLock) {            //1. 时间戳            final long now = SystemClock.uptimeMillis();            final long dueTime = now + delayMillis;            //2. 放置到“待执行队列”中,以“时间戳”进行排序的队列            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);            //3. 层层执行到DisplayEventReceiver.java的native方法            scheduleFrameLocked(now);        }    }    //Choreographer.java    private void scheduleFrameLocked(long now) {        //1. 最终都会在主线程中执行该方法(会受到“同步屏障”的保护而优先执行)        scheduleVsyncLocked();        ...    }    //Choreographer.java    private void scheduleVsyncLocked() {        //2. 层层执行        mDisplayEventReceiver.scheduleVsync();    }    //DisplayEventReceiver.java    public void scheduleVsync() {        //3. 最终会执行到native方法        nativeScheduleVsync(mReceiverPtr);    }    /**==========================================================*     * 底层VSYNC信号触发上层app进行三大流程的主要思路:     *    1. FrameDisplayEventReceiver继承自DisplayEventReceiver     *    2. VSYNC信号由“SurfaceFlinger”实现并定时发送,最终回调onVsync()方法     *    3. 通过异步Message切换到UI线程中,然后从“待执行队列”中取出Runnable     *    4. 执行TraversalRunnable的run()->doTraversal()->performTraversals()     * //Choreographer.java     *===========================================================*/    private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {        //1. 底层会回调App的onVsync()方法        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {            //2. 通过Handler切换到主线程,去执行run()方法中的doFrame()            Message msg = Message.obtain(mHandler, this);            msg.setAsynchronous(true); //异步Message以防止同步屏障的拦截,具有最高优先级            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);        }        //3. 需要在UI线程执行        public void run() {            doFrame(mTimestampNanos, mFrame);        }    }    //Choreographer.java    void doFrame(long frameTimeNanos, int frame) {        doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);    }    //Choreographer.java    void doCallbacks(int callbackType, long frameTimeNanos) {        //1. 取出“待执行队列”中的TraversalRunnable(CallbackQueue的extractDueCallbacksLocked方法)        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(                now / TimeUtils.NANOS_PER_MS);        //2. 执行TraverslaRunnable的run方法        for (CallbackRecord c = callbacks; c != null; c = c.next) {            c.run(frameTimeNanos);        }    }    //ViewRootImpl.java    final class TraversalRunnable implements Runnable {        @Override        public void run() {            doTraversal();        }    }    //ViewRootImpl.java    void doTraversal() {        if (mTraversalScheduled) {            //1. 避免一帧内重复进行刷新            mTraversalScheduled = false;            //2. 移除主线程消息队列中的同步屏障            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);            //3. 执行三大流程            performTraversals();        }    }    //ViewRootImpl.java    private void performTraversals() {        //1. View树的测量-可以不测量直接走布局和绘制        if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || ...) {            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);            layoutRequested = true;  //需要进行布局        }        //2. View树的布局-可以不布局直接走绘制        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);        if (didLayout) {            performLayout(lp, mWidth, mHeight);        }        //3. View树的绘制-可以不进行绘制        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;        if (!cancelDraw && !newSurface) {            performDraw();        }    }

8、测量、布局、绘制三大流程是否一定要都执行?

不是。
会根据条件执行部分流程。如:申请重新布局时就不会重新测量(requestLayout)
测量流程就一定需要走布局流程,但不一定走绘制流程,因为可能View处于不可见状态。

9、同步屏障的作用和原理?

  1. 同步屏障用于阻塞住所有的同步消息(底层VSYNC的回调onVsync方法提交的消息是异步消息)
  2. 用于保证界面刷新功能的performTraversals()的优先执行。
  3. 消息默认为同步消息异步消息只能由内部发送
  4. 同步屏障原理是:主线程的Looper会一直循环调用MessageQueuenext方法取出队列头部的Message执行,遇到同步屏障(一种特殊消息)后会去寻找异步消息执行。如果没有找到异步消息就会一直阻塞下去,除非将同步屏障取出,否则永远不会执行同步消息

参考资料

  1. Android 屏幕刷新机制
  2. 破译Android性能优化中的16ms问题
  3. android屏幕刷新显示机制
  4. Android Choreographer 源码分析

更多相关文章

  1. 详解Android应用开发中Intent的作用及使用方法
  2. 使用SourceInsight查看android中的native方法
  3. Android设置背景色为透明的两种方法
  4. [置顶] android软键盘弹出,会把原来的界面挤上去的问题 处理方法
  5. android 关于Location of the Android SDK has not been setup i
  6. Android中在GridView网格视图上实现item拖拽交换的方法
  7. Android中String资源文件的String.format方法(java)

随机推荐

  1. android:debuggable="false"
  2. Android(安卓)超简易Zxing框架 生成二维
  3. Android(安卓)TextView去掉paddingTop和p
  4. 爱踢门之锤子自由截屏快捷键配置(中)
  5. Android(安卓)基础总结:(十一)ContentResolv
  6. Android多模块构建合并aar解决方案
  7. android:configChanges
  8. Android文章博客收藏
  9. AndServer,一个Android端的web服务器
  10. 【Android开发问题集】布局加载器异常的