Android面试题-刷新机制详解(9题)

Android面试题之刷新机制,包括View是如何在16.7ms内完成画面刷新、注册监听ASYNC信号、同步屏障等。

本文是我一点点归纳总结的干货,但是难免有疏忽和遗漏,希望不吝赐教。
转载请注明链接:https://blog.csdn.net/feather_wch/article/details/81437689

有帮助的话请点个赞!万分感谢!

Android面试题-刷新机制详解(9题)

版本:2018/8/5-1(23:23)


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 源码分析
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猎羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值