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
相关推荐
sunnyday04267 小时前
Spring Boot 项目中使用 Dynamic Datasource 实现多数据源管理
android·spring boot·后端
幽络源小助理8 小时前
下载安装AndroidStudio配置Gradle运行第一个kotlin程序
android·开发语言·kotlin
inBuilder低代码平台8 小时前
浅谈安卓Webview从初级到高级应用
android·java·webview
豌豆学姐8 小时前
Sora2 短剧视频创作中如何保持人物一致性?角色创建接口教程
android·java·aigc·php·音视频·uniapp
白熊小北极8 小时前
Android Jetpack Compose折叠屏感知与适配
android
HelloBan8 小时前
setHintTextColor不生效
android
洞窝技术11 小时前
从0到30+:智能家居配网协议融合的实战与思考
android
QING61811 小时前
SupervisorJob子协程异常处理机制 —— 新手指南
android·kotlin·android jetpack
毕设源码-朱学姐12 小时前
【开题答辩全过程】以 基于安卓的停车位管理系统与设计为例,包含答辩的问题和答案
android
PWRJOY12 小时前
解决Flutter构建安卓项目卡在Flutter: Running Gradle task ‘assembleDebug‘...:替换国内 Maven 镜像
android·flutter·maven