深入理解Android屏幕刷新机制(一) 源码理解Choreographer

本文深入探讨了Android系统的屏幕刷新机制,包括60Hz的刷新频率和16ms的Vsync信号间隔。介绍了Choreographer如何协调应用绘制与屏幕刷新,确保流畅体验。内容涵盖丢帧原因、onDraw与屏幕刷新的关系,以及Vsync信号的处理流程,详细解析了Choreographer的工作原理,包括其创建、Vsync回调和帧处理过程。

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

一、概述

在Android中,当我们谈到 布局优化、卡顿优化 时,通常都知道 需要减少布局层级、减少主线程耗时操作,这样可以减少丢帧。这篇文章我们来理解下屏幕刷新机制,先提出几个问题,大家可以带着问题去阅读这篇文章

  • 丢帧一般是什么原因引起的?
  • Android刷新频率60帧/秒,每隔16ms调onDraw绘制一次?
  • onDraw完之后屏幕会马上刷新吗?
  • 如果界面没有重绘,还会每隔16ms刷新屏幕吗?
  • 如果在屏幕快要刷新的时候采取onDraw绘制会丢帧么?

1.1 基础概念

  • 屏幕刷新频率

一秒内屏幕刷新的次数(一秒内显示了多少帧的图像),单位 Hz(赫兹),如常见的 60 Hz。刷新频率取决于硬件的固定参数(不会变的)

  • 帧率 (Frame Rate)

表示 GPU 在一秒内绘制操作的帧数,单位 fps。例如在电影界采用 24 帧的速度足够使画面运行的非常流畅。而 Android 系统则采用更加流畅的 60 fps,即每秒钟GPU最多绘制 60 帧画面。帧率是动态变化的,例如当画面静止时,GPU 是没有绘制操作的,屏幕刷新的还是buffer中的数据,即GPU最后操作的帧数据。 对于用户来说,稳定的帧率才是好的体验,比如你玩王者荣耀,相比 fps 在 60 和 40 之间频繁变化,用户感觉更好的是稳定在 50 fps 的情况.

  • 应用刷新到屏幕流程

图片名称

1.2 Vsync

请添加图片描述

屏幕是周期性刷新的,根据这个vsync信号,这个vsync信号是一个固定频率的脉冲信号,屏幕每次收到vsync信号,就会从缓冲区里面取一帧图像出来显示; 而这个绘制是应用端发起的,随时都能发起。
此时这个图我们可以看到底下绘制的 1、2、3图,每一个绘制时间都小于每一次vsync信号时钟,只是在绘制的时候恰好不是vsync信号开始的时候,导致屏幕会重复显示 1 图像,这样子在用户使用时,用户是会明显感觉到卡顿的。

请添加图片描述

所以优化后应该是下面这张图 vsync2 ,至于如果让绘制开始的时间恰好是vsync时钟开始的时候,就要用到一个关键类 Choreographer,这个使用起来也很简单,举个例子,我们绘制时丢一个runable 给 Choreographer,然后再下一个vsync来时,Choreographer就会来处理消息,我们的绘制就交由Choreographer来处理。

// ViewRootImpl
void scheduleTraversals() {
     ...
     mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}

Choreographer 源码解析

framework 源码使用 android10 release 分支
Choreographer.java
DisplayEventReceiver.java
frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
frameworks/native/libs/gui/DisplayEventDispatcher.cpp

Choreographer 的引入,主要是配合 Vsync ,给上层 App 的渲染提供一个稳定的 Message 处理的时机,也就是 Vsync 到来的时候 ,系统通过对 Vsync 信号周期的调整,来控制每一帧绘制操作的时机. 至于为什么 Vsync 周期选择是 16.6ms (60 fps) ,是因为目前大部分手机的屏幕都是 60Hz 的刷新率,也就是 16.6ms 刷新一次,系统为了配合屏幕的刷新频率,将 Vsync 的周期也设置为 16.6 ms,每隔 16.6 ms ,Vsync 信号到来唤醒 Choreographer 来做 App 的绘制操作 ,如果每个 Vsync 周期应用都能渲染完成,那么应用的 fps 就是 60 ,给用户的感觉就是非常流畅,这就是引入 Choreographer 的主要作用。

前文回顾,在 深入理解Activity(二) ----- Activity显示原理 中,在Activity启动过程,执行完onResume后,会调用Activity.makeVisible(),层层调用后会走到scheduleTraversals()方法。

void scheduleTraversals() {
     ...
     mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}

包括在Android的动画中,最后都会调用到ViewRootImpl的scheduleTraversals()方法

二、 Choreographer启动流程

这里我们从 Choreographer的创建开始分析

public ViewRootImpl(Context context, Display display) {
    ...
    mChoreographer = Choreographer.getInstance();
    ...
}

2.1 Choreographer::getInstance

当前所在线程为UI线程,也就是常说的主线程。

  public static Choreographer getInstance() {
        return sThreadInstance.get(); //单例模式
    }
    
        private static final ThreadLocal<Choreographer> sThreadInstance =
            new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper();  //获取当前线程的Looper
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
            if (looper == Looper.getMainLooper()) {
                mMainInstance = choreographer;
            }
            return choreographer;
        }
    };
    

2.2 Choreographer构造函数 、创建

    private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;
         // 1. 初始化 FrameHandler
        mHandler = new FrameHandler(looper);
        //创建用于接收VSync信号的对象
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;
        mLastFrameTimeNanos = Long.MIN_VALUE;
        // 计算一帧的时间,Android手机屏幕是60Hz的刷新频率,就是16ms
        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
        //创建回调对象  ,每个链表存相同类型的任务:输入、动画、遍历绘制等任务
        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
        // b/68769804: For low FPS experiments.
       setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
    }
  • mLastFrameTimeNanos:是指上一次帧绘制时间点;
  • mFrameIntervalNanos:帧间时长,一般等于16.7ms.

继续看下 FrameHandler 和 FrameDisplayEventReceiver

2.3 创建FrameHandler

    private final class FrameHandler extends Handler {
        public FrameHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DO_FRAME: //开始渲染下一帧的操作
                    doFrame(System.nanoTime(), 0);
                    break;
                case MSG_DO_SCHEDULE_VSYNC://请求 Vsync 
                    doScheduleVsync();
                    break;
                case MSG_DO_SCHEDULE_CALLBACK://处理 Callback
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }

2.4 创建 FrameDisplayEventReceiver

private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
    public FrameDisplayEventReceiver(Looper looper) {
        super(looper); //【见小节2.4.1】
    }
    
       @Override
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame) { ......
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        Message msg = Message.obtain(mHandler, this);
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
    @Override
    public void run() {
         mHavePendingVsync = false;
        doFrame(mTimestampNanos, mFrame);
    }
    
    public void scheduleVsync() {
         ......  
        nativeScheduleVsync(mReceiverPtr);
        ......
    }
}

Vsync 的注册、申请、接收都是通过 FrameDisplayEventReceiver 这个类,所以可以先简单介绍一下。 FrameDisplayEventReceiver 继承 DisplayEventReceiver , 有三个比较重要的方法

  • onVsync – Vsync 信号回调
  • run – 执行 doFrame
  • scheduleVsync – 请求 Vsync 信号

这几个方法在下文还会讲到

2.4.1 DisplayEventReceiver

我们可以看到 FrameDisplayEventReceiver继承 DisplayEventReceiver,这里我们过一下DisplayEventReceiver创建过程。

public DisplayEventReceiver(Looper looper) {
    mMessageQueue = looper.getQueue(); //获取主线程的消息队列
    //【见小节2.4.2】
    mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue);
}

经过jni调用进入native方法

2.4.2 android_view_DisplayEventReceiver :: nativeInit

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject messageQueueObj, jint vsyncSource) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
            receiverWeak, messageQueue, vsyncSource);
    status_t status = receiver->initialize();
    if (status) {
        String8 message;
        message.appendFormat("Failed to initialize display event receiver.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return 0;
    }
 //获取DisplayEventReceiver对象的引用
    receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

2.4.3 android_view_DisplayEventReceiver :: NativeDisplayEventReceiver

NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
        jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource) :
        DisplayEventDispatcher(messageQueue->getLooper(),
                static_cast<ISurfaceComposer::VsyncSource>(vsyncSource)),
        mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
        mMessageQueue(messageQueue) {
    ALOGV("receiver %p ~ Initializing display event receiver.", this);
}

NativeDisplayEventReceiver继承于LooperCallback对象,此处mReceiverWeakGlobal记录的是Java层 DisplayEventReceiver对象的全局引用。

这里创建的 DisplayEventDispatcher来接受vsync信号

2.4.5 android_view_DisplayEventReceiver.cpp:: initialize

status_t status = receiver->initialize(); 我们继续看下初始化

status_t NativeDisplayEventReceiver::initialize() {
    //mReceiver为DisplayEventReceiver类型
    status_t result = mReceiver.initCheck();
    ...
    //监听mReceiver的所获取的文件句柄。
    int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
            this, NULL);
    ...

    return OK;
}

简单来说,

  1. FrameDisplayEventReceiver 的初始化过程中,通过 BitTube(本质是一个 socket pair),来传递和请求 Vsync 事件,(通过监听BitTube对象来处理相关回调)
  2. 当 SurfaceFlinger 收到 Vsync 事件之后,通过 appEventThread 将这个事件通过 BitTube 传给 DisplayEventDispatcher ,
  3. DisplayEventDispatcher 通过 BitTube 的接收端监听到 Vsync 事件之后,回调 Choreographer.FrameDisplayEventReceiver.onVsync ,触发开始一帧的绘制

图片名称

三、Vysnc回调流程

当vysnc信号由SurfaceFlinger中创建HWC触发,唤醒DispSyncThread线程,再到EventThread线程,然后再通过BitTube直接传递到目标进程所对应的目标线程,执行handleEvent方法。

3.1 android_view_DisplayEventReceiver.cpp:: NativeDisplayEventReceiver

int DisplayEventDispatcher::handleEvent(int, int events, void*) {
    ... 
    // Drain all pending events, keep the last vsync.
    nsecs_t vsyncTimestamp;
    PhysicalDisplayId vsyncDisplayId;
    uint32_t vsyncCount;
    //清除所有的pending事件,只保留最后一次vsync
    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
        ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%"
                ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
                this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
        mWaitingForVsync = false;
        //分发Vsync
        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
    }

    return 1; // keep the callback
}

接着我们看下 processPendingEventsdispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);

3.2 android_view_DisplayEventReceiver.cpp:: processPendingEvents

遍历所有的事件,当有多个VSync事件到来,则只关注最近一次的事件。

bool DisplayEventDispatcher::processPendingEvents(
        nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) {
    bool gotVsync = false;
    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
    ssize_t n;
    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
        ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
        for (ssize_t i = 0; i < n; i++) {
            const DisplayEventReceiver::Event& ev = buf[i];
            switch (ev.header.type) {
            case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
            //获取VSync信号
                gotVsync = true;
                *outTimestamp = ev.header.timestamp;
                *outDisplayId = ev.header.displayId;
                *outCount = ev.vsync.count;
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
                dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, ev.config.configId);
                break;
            default:
                ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
                break;
            }
        }
    }
    if (n < 0) {
        ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n));
    }
    return gotVsync;
}

3.3 NativeDisplayEventReceiver::dispatchVsync

void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
                                               uint32_t count) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();

    ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
    if (receiverObj.get()) {
        ALOGV("receiver %p ~ Invoking vsync handler.", this);
        env->CallVoidMethod(receiverObj.get(),
                gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count);
        ALOGV("receiver %p ~ Returned from vsync handler.", this);
    }

    mMessageQueue->raiseAndClearException(env, "dispatchVsync");
}

这里通过 env>CallVoidMethod(receiverObj.get(),gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count); 回调到 Choreographer.javaFrameDisplayEventReceiver中。

3.4 Choreographer::FrameDisplayEventReceiver

这里返回 Choreographer.java

    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;
      ...
        @Override
        public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
            long now = System.nanoTime();
            if (timestampNanos > now) { d
                timestampNanos = now;
            }

            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be "
                        + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            //该消息的callback为当前对象FrameDisplayEventReceiver
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            //此处mHandler为FrameHandler
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }
        ...
    }

可见onVsync()过程是通过FrameHandler向主线程Looper发送了一个自带callback的消息,此处callback为FrameDisplayEventReceiver。 当主线程Looper执行到该消息时,则调用FrameDisplayEventReceiver.run()方法.

Message msg = Message.obtain(mHandler, this);这里的this是一个runable对象,及对应的
FrameDisplayEventReceiver.run

3.5 FrameDisplayEventReceiver.run

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
        implements Runnable {

    @Override
    public void run() {
        mHavePendingVsync = false;
        doFrame(mTimestampNanos, mFrame); //【见小节3.4】
    }
}

3.6 Choreographer::doFrame

doFrame 函数主要做下面几件事

  1. 计算掉帧逻辑
  2. 记录帧绘制信息
  3. 执行 CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_INSETS_ANIMATION、CALLBACK_TRAVERSAL、CALLBACK_COMMIT
    @UnsupportedAppUsage
    void doFrame(long frameTimeNanos, int frame) { // frameTimeNanos表示这帧的时间戳
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                return; // mFrameScheduled=false,则直接返回。
            }
           ... 
            long intendedFrameTimeNanos = frameTimeNanos;//原本计划的绘帧时间点
            startNanos = System.nanoTime();
            final long jitterNanos = startNanos - frameTimeNanos;//计算差值,差值越大表示这帧已经延迟了
            if (jitterNanos >= mFrameIntervalNanos) {
            //如果延迟超过一个周期就来算下
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                //当掉帧个数超过30,则输出相应log
                if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                    Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                            + "The application may be doing too much work on its main thread.");//应用在主线程做了太多事了,导致绘制都绘制不了了
                }
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
                if (DEBUG_JANK) {
                    Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
                            + "which is more than the frame interval of "
                            + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                            + "Skipping " + skippedFrames + " frames and setting frame "
                            + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
                }
                frameTimeNanos = startNanos - lastFrameOffset;
            }

            if (frameTimeNanos < mLastFrameTimeNanos) {
                if (DEBUG_JANK) {
                    Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                            + "previously skipped frame.  Waiting for next vsync.");
                }
                scheduleVsyncLocked();
                return;
            }

            if (mFPSDivisor > 1) {
                long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
                if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                    scheduleVsyncLocked();
                    return;
                }
            }

            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
            mFrameScheduled = false;
            mLastFrameTimeNanos = frameTimeNanos;
        }

        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);
        }

        if (DEBUG_FRAMES) {
            final long endNanos = System.nanoTime();
            Log.d(TAG, "Frame " + frame + ": Finished, took "
                    + (endNanos - startNanos) * 0.000001f + " ms, latency "
                    + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
        }
    }

此处 frameTimeNanos 时底层Vsync信号到达的时间戳。

  1. 每调用一次 scheduleFrameLocked,则 mFrameScheduled = true,能执行一次 doframe操作,执行完 mFrameScheduled 并设置 mFrameScheduled = false
  2. 最终有4个回调类别,如下所示
  • Choreographer.CALLBACK_INPUT : 输入事件
  • Choreographer.CALLBACK_ANIMATION :动画
  • Choreographer.CALLBACK_TRAVERSAL:窗口刷新,执行measure/layout/draw操作
  • Choreographer.CALLBACK_COMMIT:遍历完成的提交操作,用来修正动画启动时间

这里我们可以看到最终调用 doCallback方法来处理相应的事件

3.7 Choreographer::doCallbacks

每个callback类型 long frameTimeNanos 都带有时间戳的,等时间戳到了才会执行

 void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
    // 我们使用“now”来确定回调何时到期,因为帧中的较早处理阶段可能会发布应在下一阶段运行的回调,例如导致动画开始的输入事件。
            final long now = System.nanoTime();
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;
       //如果需要,在提交帧时更新帧时间。只有在到达提交阶段时延迟超过2帧,我们才会更新帧时间。这确保了回调所观察的帧时间总是从一帧增加到下一帧,而不会重复。我们不希望下一帧的起始帧时间小于或等于前一帧的提交帧时间。请记住,下一帧很可能已经被安排好了,所以我们要确保提交时间总是至少落后一帧。
            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); //这里执行相应CallbackRecord的run函数
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

该方法主要功能:

  • 从队列头mHead查找CallbackRecord对象,当队列头部的callbacks对象为空或者执行时间还没到达,则直接返回;
  • 开始执行相应回调的run()方法;
  • 回收callbacks,加入对象池mCallbackPool,就是说callback一旦执行完成,则会被回收。

3.8 CallbackRecord::run

CallbackRecord 是 Choreographer的内部静态类

  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();
            }
        }
    }

这里的回调方法run()有两种执行情况:

  • 当token的数据类型为FRAME_CALLBACK_TOKEN,则执行该对象的doFrame()方法;
  • 当token为其他类型,则执行该对象的run()方法。

到这里整个vsync的回调结束。

下一帧vsync请求流程

我们看下文章开头的代码,在绘制前会发送下一个 Vsync 的申请

// ViewRootImpl
void scheduleTraversals() {
     ...
     mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}

Choreographer:: postCallbackDelayedInternal

经过一些调用会走到这里

  private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType
                    + ", action=" + action + ", token=" + token
                    + ", delayMillis=" + delayMillis);
        }

        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

发送MSG_DO_SCHEDULE_CALLBACK消息后,主线程接收后进入FrameHandler的handleMessage()操作,如下方法。

总结

首先回答下开头的几个问题

  • 丢帧一般是什么原因引起的?答:主线程有耗时操作,耽误了View的绘制,所以尽量避免在执行动画渲染的前后在主线程放入耗时操作,否则会造成卡顿感,影响用户体验
  • Android刷新频率60帧/秒,每隔16ms调onDraw绘制一次?答:这个刷新频率是指vsync信号发出的频率,但不是每次vsync发出都会去绘制,需要应用端主动发起重绘,这样才会向surfaceflinger请求接受vsync信号,这样在下次vsync信号来的时候才会去绘制。
  • onDraw完之后屏幕会马上刷新吗?答:onDraw完之后需要等下次vsync信号来了才刷新
  • 如果界面没有重绘,还会每隔16ms刷新屏幕吗?答:还一直会刷新屏幕,只不过这个画面数据一直用的是旧的
  • 如果在屏幕快要刷新的时候采取onDraw绘制会丢帧么?答:在代码中发起重绘时都是等下一个vsync来的时候才开始
  • 可通过Choreographer.getInstance().postFrameCallback()来监听帧率情况;
 public void onCreate() {
             super.onCreate();
             //在Application中使用postFrameCallback
             Choreographer.getInstance().postFrameCallback(new CustomFrameCallback(System.nanoTime()));
         }

CustomFrameCallback我们继承 Choreographer.FrameCallback来覆写相关方法。

参考

Choreographer原理

Android 基于 Choreographer 的渲染机制详解

https://blog.youkuaiyun.com/hfy8971613/article/details/108041504

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值