ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Android VSync机制与Choreographer工作原理详解

Android VSync机制与Choreographer工作原理详解 1. Android App请求VSync过程解析在Android图形系统中VSync垂直同步信号是整个渲染流程的核心驱动力。当应用需要更新UI时系统会通过VSync机制来协调各个组件的运作。让我们深入分析应用层请求VSync的具体过程。1.1 VSync基础概念VSync信号由显示设备的硬件产生通常与屏幕刷新率同步。在60Hz的屏幕上VSync每16.67ms产生一次。这个信号有两个关键作用通知SurfaceFlinger进行图层合成触发应用开始新一帧的渲染工作应用请求VSync的典型场景包括主动调用invalidate()请求重绘动画执行过程中需要连续更新窗口大小或内容发生变化时1.2 请求VSync的入口点应用通常通过ViewRootImpl发起VSync请求。当调用View的invalidate()方法时最终会调用到ViewRootImpl的scheduleTraversals()方法// ViewRootImpl.java void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled true; // 设置同步屏障优先处理异步消息 mTraversalBarrier mHandler.getLooper().postSyncBarrier(); // 通过Choreographer请求VSync mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ... } }2. Choreographer工作机制2.1 Choreographer初始化每个线程的Choreographer实例通过ThreadLocal存储通过getInstance()方法获取// Choreographer.java public static Choreographer getInstance() { return sThreadInstance.get(); } private static final ThreadLocalChoreographer sThreadInstance new ThreadLocalChoreographer() { Override protected Choreographer initialValue() { Looper looper Looper.myLooper(); return new Choreographer(looper, VSYNC_SOURCE_APP); } };初始化时会创建FrameDisplayEventReceiver这是接收VSync信号的关键组件private Choreographer(Looper looper, int vsyncSource) { mLooper looper; mHandler new FrameHandler(looper); // 创建VSync接收器 mDisplayEventReceiver USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null; ... }2.2 VSync请求流程当应用调用postCallback()方法时Choreographer会处理回调注册private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { synchronized (mLock) { // 记录回调信息 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); } } }scheduleFrameLocked()是请求VSync的核心方法private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled true; if (USE_VSYNC) { if (isRunningOnLooperThreadLocked()) { // 直接调度VSync scheduleVsyncLocked(); } else { // 切换到UI线程处理 Message msg mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } else { // 无VSync情况下的后备方案 mHandler.postDelayed(mFrameRunnable, delay); } } }3. VSync信号接收与处理3.1 FrameDisplayEventReceiver这个内部类负责与SurfaceFlinger通信并接收VSync信号private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { Override public void onVsync(long timestampNanos, long physicalDisplayId, int frame, VsyncEventData vsyncEventData) { // 记录VSync时间戳 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); } }3.2 doFrame处理流程当收到VSync信号后Choreographer会执行doFrame方法void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { // 计算帧间隔时间 long intendedFrameTimeNanos frameTimeNanos; startNanos System.nanoTime(); final long jitterNanos startNanos - frameTimeNanos; if (jitterNanos mFrameIntervalNanos) { // 处理掉帧情况 final long skippedFrames jitterNanos / mFrameIntervalNanos; if (skippedFrames SKIPPED_FRAME_WARNING_LIMIT) { Log.i(TAG, Skipped skippedFrames frames! ); } } // 更新帧时间 mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos); mFrameScheduled false; mLastFrameTimeNanos frameTimeNanos; } try { // 按优先级执行回调 mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); mFrameInfo.markAnimationsStart(); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); mFrameInfo.markPerformTraversalsStart(); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally { // 恢复状态 mFrameInfo.end(); } }4. 性能优化与问题排查4.1 常见性能问题VSync信号丢失当UI线程阻塞时会导致无法及时处理VSync信号回调堆积同一类型的回调过多会导致帧处理时间过长跨线程竞争非UI线程请求VSync需要额外的线程切换开销4.2 优化建议减少回调数量合并多个invalidate()调用// 不好的做法 view1.invalidate(); view2.invalidate(); // 好的做法 viewGroup.invalidate();使用正确的回调类型// 动画使用CALLBACK_ANIMATION mChoreographer.postCallback( Choreographer.CALLBACK_ANIMATION, mAnimationRunnable, null); // UI更新使用CALLBACK_TRAVERSAL mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);监控掉帧情况// 在Application中监控 public void onCreate() { super.onCreate(); Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() { Override public void doFrame(long frameTimeNanos) { // 计算帧间隔 long current System.nanoTime(); long diff (current - mLastFrameTime) / 1000000; if (diff 16.67f) { Log.w(FrameDrop, Frame dropped: diff ms); } mLastFrameTime current; // 继续监控下一帧 Choreographer.getInstance().postFrameCallback(this); } }); }4.3 调试技巧查看VSync请求堆栈adb shell dumpsys gfxinfo package framestats监控Choreographer日志// 在开发时启用详细日志 Choreographer.setDebugLogging(true);使用Systrace分析python systrace.py --apppackage gfx view -o trace.html在Systrace中重点关注UI线程的Choreographer#doFrame段VSYNC-app信号的时间间隔帧处理各阶段的耗时分布5. 高级应用场景5.1 自定义VSync源在Android 4.1之后应用可以监听不同的VSync源// 使用SurfaceFlinger的VSync Choreographer.getInstance(VSYNC_SOURCE_SURFACE_FLINGER) .postFrameCallback(...); // 使用应用的VSync默认 Choreographer.getInstance(VSYNC_SOURCE_APP) .postFrameCallback(...);5.2 预测性渲染通过VSyncEventData可以获取未来的VSync时间预测Override public void onVsync(long timestampNanos, long physicalDisplayId, int frame, VsyncEventData vsyncEventData) { // 获取下一个VSync时间 long nextVsync vsyncEventData.preferredFrameTimeline().expectedPresentationTime(); // 提前准备渲染内容 prepareFrame(nextVsync); }5.3 多显示器支持Android 11支持多显示器的VSync处理// 获取特定显示器的Choreographer Display display displayManager.getDisplay(displayId); Choreographer choreographer display.getChoreographer(); // 注册回调 choreographer.postFrameCallback(new FrameCallback() { Override public void doFrame(long frameTimeNanos) { // 处理特定显示器的帧 } });理解VSync请求过程对于优化Android应用性能至关重要。通过合理利用Choreographer和正确处理VSync信号可以显著提升应用的流畅度和响应速度。在实际开发中建议结合Systrace等工具持续监控VSync处理情况及时发现并解决性能瓶颈。
返回列表