Android界面显示_视图Choreographer控制

本文详细探讨了Android界面显示中的Choreographer如何控制视图的更新。通过阐述GPU/CPU帧率与屏幕消费的关系,以及Choreographer在主线程中的作用,揭示了Choreographer如何接收和处理Vsync信号,确保动画、输入事件和视图遍历的同步。同时,介绍了DisplayEventReceiver的工作原理,以及不同类型的回调类型,如输入处理、动画控制和视图遍历等。最后,提到了如何通过自定义doFrame监听来获取设备帧率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android界面显示_视图Choreographer控制
  • GPU/CPU生产帧率和屏幕消费帧率的生产者消费者模式

  • 掉帧,双缓存,三缓存

  • SurfaceFlinger进程发出vsync信号

  • Choreographer(ThreadLocal持有,即ui线程持有)负责获取Vsync同步信号并控制App线程(主线程)完成图像绘制的类

  • 主线程中初始化变量时,创建Choreographer对象,绑定主线程Looper

  • Choreographer通过DisplayEventReceiver通过JNI调用,利用Binder机制接收surfaceflinger进程底层Vsync信号,请求帧画面显示控制。在需要绘制时,发起请求。底层初始化(nativeInit),发起请求(scheduleVsync),实现回调(dispatchVsync)

  • DisplayEventReceiver收到底层各种类型的信号,在doFrame()方法中处理doCallbacks(int callbackType, long frameTimeNanos)

    void doFrame(long frameTimeNanos, int frame) {
      ...
    try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
                AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
    
                mFrameInfo.markInputHandlingStart();
                doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    
                mFrameInfo.markAnimationsStart();
                doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
                doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
    
                mFrameInfo.markPerformTraversalsStart();
                doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    
                doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
            } finally {
                AnimationUtils.unlockAnimationClock();
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
      ...
    }
    
  • callbackType类型:

    • Choreographer.CALLBACK_INPUT(控制同步处理输入Input)
    • Choreographer.CALLBACK_ANIMATION(控制同步动画Animation)
    • Choreographer.CALLBACK_TRAVERSAL(触发ViewRootImpl的doTraversal方法,实现一次遍历绘制)
    • Choreographer.CALLBACK_INSETS_ANIMATION
    • Choreographer.CALLBACK_COMMIT(遍历完成的提交操作,用来修正动画启动时间)
  • 自定义doFrame监听每一帧渲染时间,获取设备当前帧率(开源项目:Tiny Dancer)

    Choreographer.getInstance().postFrameCallback(new FPSFrameCallback());
    
    public class FPSFrameCallback implements Choreographer.FrameCallback{
    @Override
      public void doFrame(long frameTimeNanos){
          //do something
      }
    
    
  • doCallbacks代码如下:

    void doCallbacks(int callbackType, long frameTimeNanos) {
            CallbackRecord callbacks;
            synchronized (mLock) {
                // We use "now" to determine when callbacks become due because it's possible
                // for earlier processing phases in a frame to post callbacks that should run
                // in a following phase, such as an input event that causes an animation to start.
                final long now = System.nanoTime();
                callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                        now / TimeUtils.NANOS_PER_MS);
                if (callbacks == null) {
                    return;
                }
                mCallbacksRunning = true;
    
                // Update the frame time if necessary when committing the frame.
                // We only update the frame time if we are more than 2 frames late reaching
                // the commit phase.  This ensures that the frame time which is observed by the
                // callbacks will always increase from one frame to the next and never repeat.
                // We never want the next frame's starting frame time to end up being less than
                // or equal to the previous frame's commit frame time.  Keep in mind that the
                // next frame has most likely already been scheduled by now so we play it
                // safe by ensuring the commit time is always at least one frame behind.
                if (callbackType == Choreographer.CALLBACK_COMMIT) {
                    final long jitterNanos = now - frameTimeNanos;
                    Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
                    if (jitterNanos >= 2 * mFrameIntervalNanos) {
                        final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
                                + mFrameIntervalNanos;
                        if (DEBUG_JANK) {
                            Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
                                    + " ms which is more than twice the frame interval of "
                                    + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                                    + "Setting frame time to " + (lastFrameOffset * 0.000001f)
                                    + " ms in the past.");
                            mDebugPrintNextFrameTimeDelta = true;
                        }
                        frameTimeNanos = now - lastFrameOffset;
                        mLastFrameTimeNanos = frameTimeNanos;
                    }
                }
            }
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
                for (CallbackRecord c = callbacks; c != null; c = c.next) {
                    if (DEBUG_FRAMES) {
                        Log.d(TAG, "RunCallback: type=" + callbackType
                                + ", action=" + c.action + ", token=" + c.token
                                + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
                    }
                    c.run(frameTimeNanos);
                }
            } finally {
                synchronized (mLock) {
                    mCallbacksRunning = false;
                    do {
                        final CallbackRecord next = callbacks.next;
                        recycleCallbackLocked(callbacks);
                        callbacks = next;
                    } while (callbacks != null);
                }
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    

    从mCallbackQueues中取出CallbackRecord,for循环遍历执行c.run(frameTimeNanos);

  • CallbackRecord代码如下:

        private static final class CallbackRecord {
            public CallbackRecord next;
            public long dueTime;
            public Object action; // Runnable or FrameCallback
            public Object token;
    
            @UnsupportedAppUsage
            public void run(long frameTimeNanos) {
                if (token == FRAME_CALLBACK_TOKEN) {
                    ((FrameCallback)action).doFrame(frameTimeNanos);
                } else {
                    ((Runnable)action).run();
                }
            }
        }
    

    CallbackRecord的run方法中((Runnable)action).run();

  • CallbackRecord对象中的(Runnable)action是我们在ViewRootImpl类当中的InvalidateOnAnimationRunnable、mConsumedBatchedInputRunnable、mTraversalRunnable添加到Choreographer中来的。那么回到View的流程中,比如收到Vsync信号后,就会回调mTraversalRunnable的run()方法,再次发起一次measure、layout、draw流程,那么也就和Vsync信号对接上了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值