Android 12 Choreographer 深度解析

Choreographer(编排者)是 Android 系统中核心的帧时序协调组件,负责统一调度输入事件、动画、UI 绘制 的执行时机,确保所有操作对齐屏幕的 VSync(垂直同步)信号,避免掉帧、画面撕裂等问题。Android 12 在 Choreographer 与 SurfaceFlinger(SF)的交互效率、刷新率适配等方面做了优化,以下从核心流程展开详解。

一、核心背景概念

在深入流程前,先明确关键依赖:

  • VSync 信号:由 HWComposer(HWC,硬件合成器)生成,频率等于屏幕刷新率(如 60/90/120Hz),是帧渲染的 "时间基准"。
  • SurfaceFlinger(SF) :系统级服务,负责管理屏幕帧缓冲区、合成多应用图层,其内部的EventThread专门负责向应用分发 VSync 信号。
  • DisplayEventReceiver:Choreographer 与 SF 通信的底层抽象,通过 JNI+Binder 实现跨进程 VSync 信号接收。

二、App 侧 Choreographer 的创建(含与 SF EventThread 建立通道)

Choreographer 是线程单例 (绑定 Looper),UI 线程的 Choreographer 在首次调用Choreographer.getInstance()时创建, ViewRootImpl 创建的时候就会调用Choreographer.getInstance(),核心流程包含 "自身初始化" 和 "与 SF 建立 VSync 通信通道" 两步。

2.1 Choreographer 的初始化流程

1. 单例创建逻辑

每个拥有 Looper 的线程(如 UI 线程)有且仅有一个 Choreographer,源码核心逻辑:

复制代码
//ViewRootImpl.java 
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
            boolean useSfChoreographer) {
        mChoreographer = Choreographer.getInstance();
}



// Choreographer.java
private static final ThreadLocal<Choreographer> sThreadLocal = new ThreadLocal<>();

public static Choreographer getInstance() {
    return getInstance(Looper.myLooper()); // 绑定当前线程的Looper
}

public static Choreographer getInstance(Looper looper) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    Choreographer choreographer = sThreadLocal.get();
    if (choreographer == null) {
        // 首次创建时初始化
        choreographer = new Choreographer(looper);
        sThreadLocal.set(choreographer);
    }
    return choreographer;
}
2. Choreographer 构造函数(核心组件初始化)
复制代码
 private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;
        // 1. 帧处理Handler(绑定Looper,处理VSync触发的帧消息)
        mHandler = new FrameHandler(looper);

        // 2. 核心:与SF通信的DisplayEventReceiver(封装VSync接收逻辑)
        mDisplayEventReceiver = new FrameDisplayEventReceiver(looper, vsyncSource);
        mLastFrameTimeNanos = Long.MIN_VALUE;

        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

        //3. 回调队列,不同的地方都注册了vsync监听
        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));
    }

2.2 与 SF EventThread 建立通信通道(关键)

Choreographer 通过FrameDisplayEventReceiver(继承自DisplayEventReceiver)完成与 SF 的连接,核心是DisplayEventReceiver的 native 层初始化。

1. FrameDisplayEventReceiver 的创建

FrameDisplayEventReceiver是 Choreographer 的内部类,专门处理 VSync 信号的接收和转发:

复制代码
private final class FrameDisplayEventReceiver extends DisplayEventReceiver {

    public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
        super(looper, vsyncSource, 0);
    }



    // 接收SF发来的VSync信号后的回调
    @Override
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
        // 转换时间戳为毫秒,发送MSG_DO_FRAME消息到FrameHandler
        long now = System.nanoTime();
        if (timestampNanos > now) {
            timestampNanos = now;
        }
        Message msg = mHandler.obtainMessage(MSG_DO_FRAME, frame, 0, timestampNanos);
        msg.setAsynchronous(true); // Android 12优化:异步消息减少阻塞
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        mPosted = false;
    }
}
2. native 层初始化(与 SF 建立连接)

DisplayEventReceiver的构造函数会调用nativeInit,完成跨进程通信通道的建立:

复制代码
// DisplayEventReceiver.java

    public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) {

        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), 
                mMessageQueue,
                vsyncSource, eventRegistration);
    }

// 底层nativeInit逻辑(Android 12源码,简化)
// frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
jlong nativeInit(JNIEnv* env, jobject thiz, jobject weakThiz, jobject messageQueueObj, jint displayId) {
    // 1. 获取MessageQueue的native层对象(Looper的底层实现)
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    // 2. 创建DisplayEventDispatcher(核心分发器)
    sp<DisplayEventDispatcher> dispatcher = new DisplayEventDispatcher(env, weakThiz, messageQueue, displayId);
    // 3. 初始化Dispatcher:通过SurfaceComposerClient连接SF
    return reinterpret_cast<jlong>(dispatcher.get());
}

//DisplayEventReceiver.cpp
// 最终调用到

DisplayEventReceiver::DisplayEventReceiver(
        ISurfaceComposer::VsyncSource vsyncSource,
        ISurfaceComposer::EventRegistrationFlags eventRegistration) {
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if (sf != nullptr) {
        //与SF 建立IDisplayEventConnection 通道
        mEventConnection = sf->createDisplayEventConnection(vsyncSource, eventRegistration);
        if (mEventConnection != nullptr) {
            mDataChannel = std::make_unique<gui::BitTube>();
            //建立BitTube(本地套接字)
            // SF 是通过本地套接字传递vsync事件给到DisplayEventReceiver的
            mEventConnection->stealReceiveChannel(mDataChannel.get());
        }
    }
}
3. SF 侧的处理(EventThread)
  • SF 的EventThread是每个 Display 的专属线程,负责管理所有 App 的 VSync 注册请求。
  • 当 App 的DisplayEventDispatcher调用initialize()时,会通过SurfaceComposerClient向 SF 发送注册请求,EventThread会创建一个Connection对象(关联 App 的 Binder 接口),并将其加入 VSync 分发列表。
  • 至此,App 侧 Choreographer 与 SF 的 EventThread 完成双向通信通道的建立

三、Choreographer 从 SF 获取 VSync 信号的完整流程

VSync 信号的流转是 "SF 生成 → 分发 → App 接收 → 帧处理" 的闭环,Android 12 对该流程做了多维度优化(如异步消息、刷新率自适应),核心步骤如下:

3.1 触发 VSync 请求(App 侧主动发起)

App 侧需要渲染新帧时(如View.postInvalidate()ValueAnimator动画)是, 用通过Choreographer.postCallback() 或postFrameCallback()申请下个vsync。 调用 mDisplayEventReceiver.scheduleVsync(),最终native的DisplayEventReceiver 同binder 调用的SF requestNextVsync()

复制代码
// ViewRootImpl.java

    void invalidate() {
        mDirty.set(0, 0, mWidth, mHeight);
        if (!mWillDrawSoon) {
            scheduleTraversals();
        }
    }

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

// Choreographer.java

    public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }


   postCallbackDelayed()
   postCallbackDelayedInternal();
   scheduleVsyncLocked();
   mDisplayEventReceiver.scheduleVsync();



//  DisplayEventReceiver.cpp

status_t DisplayEventReceiver::requestNextVsync() {
    if (mEventConnection != nullptr) {
        mEventConnection->requestNextVsync();
        return NO_ERROR;
    }
    return NO_INIT;
}

// 最终会通过DisplayEventReceiver的binder 

.... binder .....

// SF 端
//EventThread.cpp
void EventThreadConnection::requestNextVsync() {
    mEventThread->requestNextVsync(this);
}

void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {

    if (connection->vsyncRequest == VSyncRequest::None) {
        connection->vsyncRequest = VSyncRequest::Single;
        mCondition.notify_all();  //唤醒EventThread的线程,监听下一个vsync
    } 
}

void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {

        if (mState != nextState) {
            if (mState == State::VSync) {
                mVSyncSource->setVSyncEnabled(false);
            } else if (nextState == State::VSync) {
                mVSyncSource->setVSyncEnabled(true);  //监听下一个vsync
            }

            mState = nextState;
        }

}

3.2 SF 生成并分发 VSync 信号

1. VSync 信号的生成

SF 的VSyncThread监听 HWC 的 VSync 中断:HWC 根据屏幕刷新率(如 60Hz)生成硬件级 VSync 信号,触发VSyncThread的回调。(Vsync 不一定是HWC上传的,可能是FS VSyncPredictor根据)

2. EventThread 分发 VSync
  • VSyncThread将 VSync 事件推送给对应 Display 的EventThread
  • EventThread遍历其管理的所有Connection(即已注册 VSync 的 App),通过 Binder 跨进程将 VSync 事件(包含时间戳、帧号、物理屏 ID)发送给 App 侧的DisplayEventDispatcher

3.3 App 侧接收 VSync 信号

  • DisplayEventDispatcher通过 epoll 监听 SF 的 Binder 消息,接收到 VSync 事件后,调用onVsync回调(Java 层FrameDisplayEventReceiver.onVsync)。
  • FrameDisplayEventReceiver将 VSync 时间戳封装为MSG_DO_FRAME消息,发送给FrameHandler(Android 12 新增:消息标记为异步,避免被同步消息阻塞)。

3.4 Choreographer 处理帧逻辑(doFrame)

FrameHandler处理MSG_DO_FRAME时,调用Choreographer.doFrame(),按优先级执行回调:

复制代码
void doFrame(long frameTimeNanos, int frame) {
    long startNanos = System.nanoTime();
    try {
        // 1. 处理输入回调(最高优先级:触摸/按键等)
        doCallbacks(CALLBACK_INPUT, frameTimeNanos);
        // 2. 处理动画回调(第二优先级:ValueAnimator/属性动画)
        doCallbacks(CALLBACK_ANIMATION, frameTimeNanos);
        // 3. 处理Traversal回调(最低优先级:measure/layout/draw)
        doCallbacks(CALLBACK_TRAVERSAL, frameTimeNanos);
        // 4. 处理提交回调(Android 12新增:帧提交到SF后的回调)
        doCallbacks(CALLBACK_COMMIT, frameTimeNanos);
    } finally {
        // 标记帧处理完成
        mFrameScheduled = false;
    }

    // Android 12优化:帧耗时统计,超过阈值则标记掉帧
    long frameDurationNanos = System.nanoTime() - startNanos;
    if (frameDurationNanos > mFrameIntervalNanos) {
        long overrunNanos = frameDurationNanos - mFrameIntervalNanos;
        Log.w(TAG, "Frame time is " + overrunNanos / 1000000 + "ms overrun!");
    }
}

3.5 关键补充:Android 12 的 VSync 优化

  1. 异步 MSG_DO_FRAME:避免 VSync 消息被同步消息(如 Handler.post)阻塞,减少帧延迟。
  2. 动态刷新率(DRR)适配 :Choreographer 的RefreshRateTracker实时监测帧需求,自动请求 SF 切换刷新率(如静态 UI 时降为 60Hz,动画时升为 120Hz)。
  3. 减少 JNI 调用开销DisplayEventDispatcher的 native 层逻辑优化,合并多次 Binder 调用,降低跨层耗时。
  4. 多屏 VSync 支持DisplayEventReceiver支持指定displayId,适配折叠屏 / 多屏场景的 VSync 分发。

四、核心总结

4.1 关键流程闭环

复制代码
App触发帧需求 → Choreographer.postCallback → FrameDisplayEventReceiver.scheduleVsync()
→ SF EventThread注册VSync请求 → HWC生成VSync → SF EventThread分发VSync
→ App侧DisplayEventReceiver接收VSync → FrameHandler发送MSG_DO_FRAME
→ Choreographer.doFrame()执行输入/动画/绘制回调 → 帧渲染完成提交到SF

4.2 核心设计要点

  1. 线程单例:Choreographer 绑定 Looper,确保每个线程的帧时序独立,避免多线程干扰。
  2. 优先级调度:输入 > 动画 > 绘制,保障交互响应优先于 UI 渲染。
  3. VSync 对齐:所有帧操作严格对齐 VSync,避免掉帧和画面撕裂。
  4. 跨进程通信:通过 DisplayEventReceiver+Binder 实现 App 与 SF 的 VSync 信号传递。

4.3 常见问题解析

  • 掉帧原因doFrame执行时间超过一帧时长(如 60Hz 下 16.6ms),导致下一个 VSync 到来时未完成当前帧。
  • VSync 丢失:SF 的 EventThread 分发延迟,或 App 侧 Looper 阻塞(如主线程耗时操作)。
  • Android 12 流畅度提升:异步消息、刷新率自适应、JNI 优化共同减少帧延迟和掉帧概率。

五、源码参考路径(Android 12)

  • Choreographer:frameworks/base/core/java/android/view/Choreographer.java
  • DisplayEventReceiver:frameworks/base/core/java/android/view/DisplayEventReceiver.java
  • native 层 DisplayEventDispatcher:frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
  • SF EventThread:frameworks/native/services/surfaceflinger/EventThread.cpp
  • VSyncThread:frameworks/native/services/surfaceflinger/VSyncThread.cpp
相关推荐
limingade14 小时前
Android应用如何点击桌面图标开启悬浮窗后跳转到最近打开的应用
android·悬浮窗自动切换顶层应用·android静默跳转应用·安卓获取手机任务窗口列表·android代码切换应用·adb切换应用
开酒不喝车14 小时前
中间件AIDL HIDL区别总结
android·中间件
mit6.82414 小时前
Android HAL(硬件抽象层):内核到应用的完整实现
android
鹏多多15 小时前
Flutter自定义日历table_calendar完全指南+案例
android·前端·flutter
侦探观察15 小时前
南非女性旅游绑架风险分析及防范措施
android·大数据·开发语言·百度·网络安全·旅游
Digitally15 小时前
如何将文件从电脑传输到三星平板 [5种方法]
android
jie_075415 小时前
scrcpy低延迟控制 Android 设备,无需 root 权限,开源免费
android
2501_9159184115 小时前
iOS 应用如何防止破解?从逆向链路还原攻击者视角,构建完整的反破解工程实践体系
android·macos·ios·小程序·uni-app·cocoa·iphone
成都证图科技有限公司15 小时前
安卓系统Chrome内核:Android System WebView
android·前端·chrome