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
相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui