This article introduces Choreographer, a class that App developers may not frequently encounter but is critically important in the Android Framework rendering pipeline. We will cover the background of its introduction, a brief overview, partial source code analysis, its interaction with MessageQueue, its application in APM (Application Performance Monitoring), and some optimization ideas for Choreographer by mobile phone manufacturers.
Choreographer coordinates pending frame callbacks with display timing; it does not itself set the screen refresh rate. At 120 Hz, the interval is about 8.33 ms. Meeting a frame deadline also depends on CPU scheduling, GPU work, buffers, and composition. Apps request callbacks as needed rather than redraw on every physical Vsync. This article also distinguishes what FrameCallback, FrameMetrics, gfxinfo, Looper, and Perfetto can observe.
Version note (reviewed October 2026): Historical MethodTrace/Systrace and newer Perfetto images are retained. Without their original traces, interpretation is limited to what is visible. Older code skeletons use android-10.0.0_r1; excerpts involving VsyncEventData, FrameInfo, and newer callbacks are labeled against android-16.0.0_r1. BLAST discussion uses Android 15 source. Excerpts omit surrounding fields, checks, and branches: they are not standalone app code or one complete mainline implementation.
Table of Contents
- Perfetto Series Catalog
- The Essence of Main Thread Execution
- Introduction to Choreographer
- Source Code Analysis
- MessageQueue and Choreographer
- APM and Choreographer
- Manufacturer Optimizations
- Reference Materials
- Zhihu Address
- About Me & Blog
This is the fifth article in the Perfetto series, mainly giving a brief introduction to Choreographer in Perfetto.
The purpose of this series is to view the overall operation of the Android system from another perspective through the Perfetto tool, and also to learn about the Framework from a different angle. Maybe you have read many articles about the Framework, but always can’t remember the code, or are not clear about its running process. Perhaps from the graphical perspective of Perfetto, you can understand it more deeply.
Perfetto Series Catalog
- Android Perfetto Series Catalog
- Android Perfetto Series 1: Introduction to Perfetto
- Android Perfetto Series 2: Perfetto Trace Capture
- Android Perfetto Series 3: Familiarizing with Perfetto View
- Android Perfetto Series 4: Opening Large Traces Locally with Command Line
- Android Perfetto Series 5: Android App Choreographer-based Rendering Flow
- Android Perfetto Series 6: Why 120Hz? Advantages and Challenges of High Refresh Rate
- Android Perfetto Series 7: MainThread and RenderThread Interpretation
- Android Perfetto Series 8: Indepth Understanding of Vsync Mechanism and Performance Analysis
- Android Perfetto Series 9: CPU Information Interpretation
- Android Perfetto Series 10: Binder Scheduling and Lock Contention
- Android Perfetto Series 11: PerfettoSQL, Trace Processor and Regression Detection
- Android Perfetto Series 12: Trace Dataflow and Data Loss
- Android Perfetto Series 13: Perfetto SDK, Track Event and App Field Traces
- Android Perfetto Series 14: heapprofd and Memory Profiling
- Android Perfetto Series 15: Boot Traces and Long-running Field Tracing
- Android Perfetto Series 16: GPU, Power Counters and Hardware Bottlenecks
- Android Perfetto Series 17: Scenario Automation and Platform Tracing
- Android Perfetto Series 18: Input Response Latency
- Video (Bilibili) - Android Perfetto Basics and Case Sharing
- Video (Bilibili) - Android Perfetto: Trace Graph Types - AOSP, WebView, Flutter + OEM System Optimization
If you haven’t seen the Systrace series yet, here is the portal:
- Systrace Series Catalog: Systematically introduces the use of Systrace, the predecessor of Perfetto, and learns and understands the basic rules of Android performance optimization and Android system operation through Systrace.
- Systrace version of this article: Detailed Explanation of Android Choreographer-based Rendering Mechanism
- Personal Blog: Personal blog, mainly Android-related content, also contains some life and work-related content.
Everyone is welcome to find ways to get in touch on the About Me page to discuss your questions, the parts of Perfetto you most want to see, and discuss all Android development-related content with other group members.
The Essence of Main Thread Execution
Much Java work on the main thread is dispatched by Looper through Handlers. MessageQueue queues and retrieves messages rather than executing business logic. nativePollOnce also processes file-descriptor events, including Vsync reception; not every main-thread operation is a Java Message. These images show waiting and dispatch in the captured examples.
MethodTrace

Perfetto

Evolution
The original text described these as pre-Vsync rendering, but they already contain Choreographer.doFrame and RenderThread. They illustrate continuous work under an unspecified historical configuration, not Android before Choreographer.


A dequeueBuffer wait can involve available buffers, consumer release, or fences. The image does not establish a fixed SurfaceFlinger consumption rate for every app; a traditional framebuffer description is not universal to modern displays.
Choreographer is available from API 16. Vsync pacing and buffering help organize rendering without guaranteeing a stable frame rate. BLAST later changes the buffer/transaction path, discussed below.
For users, a stable frame rate is a good experience. For example, when playing Honor of Kings, compared to frequent fluctuations between 60 and 40 fps, users feel better when it is stable at 50 fps.
This is an intuitive description of experience. Presentation cadence matters too: a fixed 60 Hz display cannot present 50 fps at equal integer-refresh intervals, so it is not a universal ranking for every device.
Introducing Choreographer
Choreographer gives due animation and traversal callbacks an opportunity to run in relation to display timing. A software Vsync notification still depends on the owning Looper’s scheduling; a hardware interrupt does not directly execute Java drawing.
This older 60 Hz diagram uses 16.6 ms, not a universal end-to-end budget. Its RenderThread “GPU” label is inaccurate: RenderThread is a CPU thread organizing/submitting work, with asynchronous GPU execution. UI, RenderThread, GPU, and SurfaceFlinger stages can overlap.

At 120 Hz, adjacent refreshes are about 8.33 ms apart; scheduling determines the actual work deadlines. See the 90 Hz article for refresh-rate versus frame-rate context.
Introduction to Choreographer
Choreographer coordinates callbacks registered with it, not all Handler messages. Framework work is processed in Input, Animation, Insets Animation, Traversal, and Commit order when callbacks are due. These are execution phases, not Linux thread priorities.
DisplayEventReceiver provides requested software Vsync timing. Timely app, RenderThread, GPU, and SurfaceFlinger completion still needs separate observation; using Choreographer and BLAST does not guarantee stable 60/90/120 fps. Monitoring boundaries are discussed below.
Diagrams help explain the mechanism, but here I will mostly use Perfetto and MethodTrace. Placing CPU, SurfaceFlinger, SystemServer, and app activity on a timeline and following the code makes their interactions easier to see. I will use a few diagrams, then work through traces; what they show still depends on what was recorded.
Choreographer’s Workflow from the Perspective of Perfetto
These historical Perfetto images show scrolling Settings. Green Running segments are CPU scheduling states, not presented frames; slices can include waits.
- The
VSYNC-appcounter toggles to indicate software Vsync cadence, annotated around 8.33 ms here. High/low levels are not durations of “Vsync executing”; do not mistake a whole square-wave cycle for one refresh interval. Match the producer’s definition. - Follow doFrame, DrawFrame, buffer submission, and SurfaceFlinger dependencies, while accounting for overlapping threads/frames. Adding UI and RenderThread wall durations does not yield final frame latency.
- BLAST submits buffers with transactions. Submission does not prove GPU completion or presentation; inspect fences, composition, and presentation evidence.

Now zoom in on one main-thread frame to see where Choreographer sits and how it organizes the callbacks. The arrows locate that work chain. The old “queueBuffer means GPU starts” annotation is not valid: GPU submission/execution has no such fixed one-to-one relationship with queueBuffer. The end of the main-thread slice is not final presentation time either.

Choreographer’s Workflow
getInstance()obtains the default thread-local instance on a thread with a Looper, initializing its Handler, DisplayEventReceiver, and callback queues.- Due work requests Vsync. A file-descriptor event reaches
onVsync()through JNI, which posts an asynchronous Message. - When Looper dispatches it,
run()callsdoFrame()to handle timing, due callbacks, and diagnostic markers. - Input → Animation → Insets Animation → Traversal → Commit run in order. Traversal may measure/layout/draw and synchronize with RenderThread; not every frame repeats every operation.
- RenderThread/GPU work, BLAST submission, and SurfaceFlinger composition can overlap this sequence. Commit is not a GPU-completion or presentation callback.
- Remaining animation/scroll work registers another callback. A single FrameCallback is not an indefinitely repeating callback.
This older MethodTrace helps locate calls, not establish identical signatures or all phases on every frame.

Interaction between Choreographer, RenderThread and BlastBufferQueue
The following uses Android 15’s common ViewRootImpl window path. It is not a universal implementation for every release, Surface type, or vendor build.
BlastBufferQueue Working Principle
Android 15 BLASTBufferQueue maintains producer/consumer endpoints in the client and puts acquired buffers into SurfaceControl transactions. This changes buffer/window transaction coordination with SurfaceFlinger, without eliminating buffer availability, fence, or back-pressure waits.
ViewRootImpl creates and updates the queue; a changed SurfaceControl may require recreation. The retained skeleton below is not the complete relayout condition:
1 | // Example code for creating BBQ |
UI/RenderThread synchronization also depends on HWUI DrawFrameTask, uploads, and queued work. BLAST alone does not prove earlier UI return or fewer stalls; use a same-scenario trace or measurement. See ANR part three for a related wait-chain example.
Source Code Analysis
These are call relationships and selected excerpts. Older registration, animation, and traversal skeletons use Android 10 Choreographer; constructor, FrameHandler, VsyncEventData, and doFrame marker/phase examples are updated against Android 16. Omitted fields, checks, and locals mean the snippets cannot be assembled into a compilable class. Native registration is explained with Android 10 source.
Choreographer Initialization
Choreographer Singleton Initialization
1 | // Thread local storage for the choreographer. |
Choreographer Constructor
Android 16 retains the two-argument constructor, delegating to the three-argument version with layerHandle. Partial initialization:
1 | private Choreographer(Looper looper, int vsyncSource) { |
FrameHandler
1 | private final class FrameHandler extends Handler { |
In Android 16, MSG_DO_FRAME is the timer fallback without Vsync. Normal Vsync delivery uses the Receiver Runnable below.
Choreographer Initialization Chain
Adding an Activity window can construct ViewRootImpl, which obtains a Choreographer. This older call-chain illustration is not a promise of creation after every onResume, nor the only initialization path: animation code may call getInstance() earlier.
1 | ActivityThread.handleResumeActivity(IBinder, boolean, boolean, String) (android.app) |
Introduction to FrameDisplayEventReceiver
In Android 16, onVsync() saves timing and copies event data before posting an asynchronous Message; run() later calls doFrame(). scheduleVsync() is inherited from DisplayEventReceiver, which owns the native pointer. Checks and surrounding fields are omitted below:
1 | private final class FrameDisplayEventReceiver extends DisplayEventReceiver |
Vsync Registration in Choreographer
How does Vsync arrival lead to onVsync()? Follow initialization to see how the file descriptor is registered. These three registration skeletons are from Android 10, not signatures to combine with the Android 16 Receiver above. DisplayEventReceiver establishes a native connection through JNI and registers the receiving FD with the native Looper.
1 | private Choreographer(Looper looper, int vsyncSource) { |
1 | public FrameDisplayEventReceiver(Looper looper, int vsyncSource) { |
1 | public DisplayEventReceiver(Looper looper, int vsyncSource) { |
Vsync event data travels through BitTube (a socket pair), while requesting the next notification uses Binder IDisplayEventConnection.requestNextVsync(). The image separates nativePollOnce→onVsync enqueueing from later Message dispatch→run/doFrame.

DisplayEventReceiver Communication Details with SurfaceFlinger
In Android 10’s native DisplayEventReceiver, the client creates a connection and obtains the BitTube receiving endpoint through Binder stealReceiveChannel(). Control requests and event delivery are separate; the app is not directly requesting a hardware interrupt through the socket:
1 | App / DisplayEventReceiver SurfaceFlinger / EventThread |
FD monitoring integrates with Looper, but scheduling and queued work still affect delivery. This is not a real-time execution guarantee.
Logic of Choreographer Processing a Frame
Normal Vsync delivery posts the Receiver Runnable from onVsync(); Looper later dispatches run()/doFrame(). This repeats the Android 16 core excerpt with timestamp and pending-state checks omitted:
1 |
|
doFrame() checks scheduled work, handles lateness relative to the supplied frame time, updates frame metadata, and executes due phase callbacks. Its skippedFrames value estimates start delay, not final display drops.
Recording Frame Drawing Info
android.graphics.FrameInfo is hidden internal Framework/HWUI data recording selected phase boundaries, not every function or callback duration. These Android 16 fields stop at FRAME_INTERVAL; surrounding declarations such as FLAGS and the later WORKLOAD_TARGET are omitted. The old gfxinfo CSV below has a different header—do not decode it with these newer indices.
1 |
|
Android 16’s six-argument setVsync() includes vsyncId, deadline, start time, and interval. Markers precede their corresponding callback phases, simplified here:
1 | void doFrame(long frameTimeNanos, int frame, VsyncEventData vsyncEventData) { |
Executing Callbacks
The second doCallbacks() argument here is Android 16’s frameIntervalNanos, not the older frameTimeNanos. The Input/Animation/Traversal paths below are older skeletons and historical stacks; each phase extracts only due callbacks.
1 | void doFrame(long frameTimeNanos, int frame, VsyncEventData vsyncEventData) { |
Input Callback Call Stack
input callback generally executes ViewRootImpl.ConsumeBatchedInputRunnable
android/view/ViewRootImpl.java
1 | final class ConsumeBatchedInputRunnable implements Runnable { |
The image shows one batched touch-input path through ViewRootImpl to DecorView/Activity/Views. Keys and non-batched input need not wait for CALLBACK_INPUT, and not every input event calls dispatchTouchEvent.

Animation Callback Call Stack
View.postOnAnimation posts work to CALLBACK_ANIMATION.
1 | public void postOnAnimation(Runnable action) { |
Which code calls View.postOnAnimation? I captured the call hierarchy below; it includes operations such as startScroll and Fling.

The static hierarchy includes fling callers, but the following runtime stack shows AnimationHandler, ValueAnimator/AnimatorSet, and property updates. It does not identify a specific FlingRunnable by itself.

In addition, our Choreographer‘s FrameCallback also uses CALLBACK_ANIMATION.
1 | public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) { |
Traversal Call Stack
The following omits surrounding locals and branches, illustrating schedule→doTraversal→performTraversals. Measure, layout, and draw are conditional. The barrier is removed before performTraversals, not after GPU or display completion.
1 | void scheduleTraversals() { |
TraceView Example of doTraversal

Next Frame Vsync Request
Due to the existence of operations such as animation, sliding, and Fling, we need a continuous and stable frame rate output mechanism. This involves the request logic of Vsync. In continuous operations, such as animation, sliding, and Fling, doFrame of each frame will trigger the application for the next Vsync according to the situation, so that we can obtain continuous Vsync signals.
See the call stack of scheduleTraversals below (scheduleTraversals will trigger Vsync request)
invalidate() and requestLayout() can schedule traversal, but visibility, an existing traversal, and mFrameScheduled coalesce work. One call does not imply one independent Vsync request.
Let’s take Animation as an example to see how Animation drives the next Vsync to continuously update the screen.
ObjectAnimator Animation Driving Logic
android/animation/ObjectAnimator.java
1 | public void start() { |
android/animation/ValueAnimator.java
1 | private void start(boolean playBackwards) { |
android/animation/AnimationHandler.java
1 | public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) { |
Calling postFrameCallback will go to mChoreographer.postFrameCallback, where the Vsync request logic of Choreographer will be triggered.
android/animation/AnimationHandler.java
1 | public void postFrameCallback(Choreographer.FrameCallback callback) { |
android/view/Choreographer.java
1 | private void postCallbackDelayedInternal(int callbackType, |
AnimationHandler reposts while animation callbacks remain. FrameCallback itself is one-shot; delayed or absent work does not imply a callback on every Vsync.
TraceView Example of a Frame During Animation

Source Code Summary
getInstance()returns the default instance for the current Looper thread. It is not process-global or a ban on other internal Choreographer instances on that thread.- Binder control, BitTube events, native FD callbacks, and Java asynchronous Messages are distinct steps, not direct Java execution of a hardware interrupt.
- The five-phase order lets due input/animation changes precede traversal. COMMIT is not a GPU fence or presentation notification.
- ListView/RecyclerView layout, binding, scrolling, and prefetch can occur through different paths. ViewHolder reuse is not a separate Choreographer phase or proof that one component is always faster; inspect its library version and trace.
- FrameCallback is removed after one invocation. Re-registration sustains animation; scheduling coordination does not guarantee timely callbacks or presentation of every buffer.
APM and Choreographer
Distinguish callback cadence, Window/HWUI work, SurfaceFlinger presentation, and one Looper dispatch. They are different metrics. FrameInfo is hidden internal data, not a stable reflection-based app interface.
- API 16 FrameCallback: observes callback cadence; a long interval suggests delay, not measured display fps.
- API 24 FrameMetrics: delivers window frame reports. The listener’s
dropCountSinceLastInvocationcounts lost reports, not display drops. Copy the reused metrics object before deferred processing. - API 33 postVsyncCallback: supplies FrameData timelines, deadlines, and expected presentation information. It is one-shot and predictive, not proof of presentation. See Choreographer.
- gfxinfo/FrameTimeline: read version-specific HWUI output; Android 12+ FrameTimeline relates expected/actual work and presentation for jank analysis when its data source is collected.
- Looper/IdleHandler: dispatch wall time and queue idleness aid diagnosis, but do not cover GPU/presentation. IdleHandler is not an end-of-frame callback.
Historical examples below are retained with their limits.
Use Perfetto for Advanced Monitoring
Perfetto aligns enabled scheduling, CPU frequency, app trace sections, graphics, and service data. Categories, app tracing permissions, and device support determine coverage; slices require instrumentation. Sampling, stacks, and graphics sources have their own costs, so lower overhead cannot be guaranteed without a configuration comparison.
Use Perfetto to Track Choreographer
Replace com.example.app with the target package. Perform the interaction while the foreground command records for 15 seconds, then pull after it exits. The 32 MB ring buffer is an example, not guaranteed full-window coverage. Categories vary by device/version.
1 | # Start recording Perfetto trace (example: 15s with common categories) |
Search for Choreographer#doFrame (possibly with a frame ID). It measures Choreographer callback processing, not full GPU/presentation latency. Configure android.surfaceflinger.frametimeline separately when needed; the shorthand command does not guarantee it. See the capture article for a full configuration.
Use framestats (internally backed by FrameInfo) for Monitoring
Use adb shell dumpsys gfxinfo <packagename> framestats, replacing the package. This is the original historical StatusBar output, not a new capture:
1 | Window: StatusBar |
Parse the CSV by its own header. Timestamp fields differ from durations such as DequeueBufferDuration/QueueBufferDuration; units are ns, with monotonic rather than calendar time. In this older format, OldestInputEvent=9223372036854775807 and NewestInputEvent=0 are unrecorded-input sentinels, not values for calculating input latency.
FrameCompleted marks completion in that HWUI path, not physical presentation. Summary categories such as Missed Vsync and Slow UI can overlap and must not be summed as independent frames. This sample is not Android 16’s FrameInfo layout.
Use SurfaceFlinger for Monitoring
On older implementations, find a layer with adb shell dumpsys SurfaceFlinger --list, then query its latency, for example adb shell 'dumpsys SurfaceFlinger --latency "actual layer name"'. Bare --latency does not select an app; implementation and permissions vary.
In Android 10 FrameTracker, the first line is refresh period and the columns are desired present, actual present, and frame ready times in ns. Its ring has 128 slots but the dump iterates 127, not 128 guaranteed new valid frames. Identify zero and pending-fence INT64_MAX records as invalid/incomplete.
The old ceil((C - A) / refresh_period) uses ready minus desired time without actual presentation. It is not a general dropped-frame formula; refresh changes, pipeline deadlines, and missing values also matter. Prefer FrameTimeline frame/presentation correlation for modern analysis.
Use SurfaceFlinger PageFlip Mechanism for Monitoring
Obsolete interface—historical explanation only. Android 10’s private transaction 1013 returned the default display’s page-flip count under system UID/HARDWARE_TEST checks, not app fps. Android 16 returns NAME_NOT_FOUND for the same code. The original transact-then-readInt snippet ignored failure and is no longer presented as a usable monitoring example.
1 | // android-10.0.0_r1: selected SurfaceFlinger::onTransact branch |
These are separate version excerpts, not one switch. Use FrameMetrics or FrameTimeline for the intended measurement. Unsupported, denied, or missing data must not become 0 fps. See Android 16 SurfaceFlinger.
Choreographer’s Own Dropped Frame Calculation Logic
This is an older simplified excerpt. jitterNanos is start delay relative to the supplied frame time. Dividing by the interval estimates missed scheduling opportunities, not the current doFrame duration or final presentation drops. Android 16 uses the event interval and handles zero-interval cases.
1 | if (jitterNanos >= mFrameIntervalNanos) { |
SKIPPED_FRAME_WARNING_LIMIT defaults to 30 and is read from the property at class initialization. No warning does not mean no jank, and changing its threshold does not fix jank.
BlockCanary
BlockCanary historically used Looper MessageLogging to observe dispatch boundaries. The older skeleton below illustrates their placement; newer implementations may use loopOnce. This is not an installation or maintenance recommendation for the library.
1 | public static void loop() { |
Dispatch wall time includes blocking and scheduling delay, not just CPU work. It does not cover all nativePollOnce FD handling, GPU work, or presentation. Many short messages can also delay a frame. Logging costs work and must be coordinated with other users of the same Looper logger.
MessageQueue and Choreographer
A synchronization barrier changes queue eligibility, not CPU priority. In Android 10 MessageQueue.next(), a barrier at the head (null target) causes a search for the next asynchronous message, whose due time is still checked. This does not preempt a running callback or create another thread.
Framework code uses the hidden postSyncBarrier()/removeSyncBarrier() methods. Message.setAsynchronous(boolean) is public from API 22, and Handler.createAsync() from API 28; the older hidden-API claim was wrong. Without an obstructing barrier, the asynchronous flag does not automatically move a message to the front, and cross-Handler ordering dependencies still matter.
An Example of SyncBarrier Used in Choreographer
scheduleTraversals calls postSyncBarrier.
1 | void scheduleTraversals() { |
doTraversal calls removeSyncBarrier.
1 | void doTraversal() { |
These frame-scheduling Messages are asynchronous so they can pass an active barrier. This does not raise Linux scheduling priority or make them precede all other messages.
1 | Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); |
Manufacturer Optimizations
System manufacturers can change framework code and try optimizations at this level. I cannot share that code, but I can outline the ideas. If you are interested, we can discuss them privately.
These are ideas I was considering at the time, not default AOSP behavior or validated benefits on every build. Changes must preserve input/lifecycle order, animation timebases, and buffer/fence contracts, with latency, presentation, power, and compatibility measured.
Input Event Optimization
Choreographer already has CALLBACK_INPUT, used by ViewRootImpl for batched input. Vendors may use more input state to schedule earlier, but must distinguish reception, batching, and app callbacks, validating ordering, resampling, and input-to-presentation latency.
Background Animation Optimization
Stop animation updates when they are invisible and unnecessary for the product, using lifecycle/visibility information. Vendor throttling needs similar care: background does not automatically make every callback useless, especially with multi-window, picture-in-picture, or state progression. This historical image shows dense doFrame work that I labeled as a background case; it does not independently establish visibility, power cost, firmware configuration, or the app’s current behavior.

Frame Drawing Optimization
A vendor can explore earlier composition scheduling in selected scenarios. That does not let a display present at arbitrary times: buffer/fence dependencies, display deadlines, and power constraints still apply. Earlier composition alone is not proof of fewer missed frames.
App Launch Optimization
If queued work delays critical startup messages, first identify unrelated work and waits, then consider controlled scheduling. Arbitrary front-of-queue movement can break lifecycle/input order or starve work. Validate first-frame/interactivity and regressions, not just an earlier message timestamp.
Animation Callback Front-loading
There may be slack after main-thread work, but that does not prove CPU idleness; other messages, threads, and GPU work can continue. Earlier animation calculation must preserve frameTime, property-update, and input-sampling consistency. It is a specific framework strategy, not an automatic jank reduction.
Frame Interpolation
This is closer to vendor pre-rendering or prediction of later animation states than a universal definition of frame interpolation. doFrame has scheduled-state, monotonic-time, and callback-queue constraints. Calling it twice does not establish two generated, submitted, or presented frames, nor image-based motion interpolation.
OverScroller predicts from current velocity and bounds, but new input, boundaries, layout, or content changes can invalidate that prediction. Any advance work needs cancellation, correct timing, buffer/power accounting, and presentation validation. Touch-up does not fix all later frame content.
High Refresh Rate Optimization
When the active display mode is 120 Hz, refreshes are about 8.33 ms apart rather than 16.67 ms at 60 Hz; app frame rate may differ. Evaluate deadlines, presentation cadence, and energy instead of treating the whole pipeline as a fixed single-thread budget. Vendor priorities include:
- Performance analysis and optimization of large apps
- Game high frame rate cooperation
- Logic of mutual switching between 120 fps, 90 fps and 60 fps
Reference Materials
- https://www.jianshu.com/p/304f56f5d486
- http://gityuan.com/2017/02/25/choreographer/
- https://developer.android.com/reference/android/view/Choreographer
- https://www.jishuwen.com/d/2Vcc
- https://juejin.im/entry/5c8772eee51d456cda2e8099
- Android Development Expert Class
- Perfetto - System profiling, app tracing, and trace analysis
- Official Perfetto UI guide
- BufferQueue and Gralloc
- Android 15 Official Features: Graphics
Zhihu Address
Since blog comments are inconvenient, if you want to like or communicate, please move to the Zhihu page of this article
Zhihu - Detailed Explanation of Android Choreographer-based Rendering Mechanism - Perfetto Version
Juejin - Detailed Explanation of Android Choreographer-based Rendering Mechanism - Perfetto Version
About Me & Blog
Below is a personal introduction and related links. I look forward to communicating with you all!
- Blogger Personal Introduction: Contains personal WeChat and WeChat group links.
- Blog Content Navigation: A navigation of personal blog content.
- Excellent Blog Articles Collected and Organized by Individuals - Must-Know for Android Performance Optimization: Welcome everyone to recommend yourself and recommend (WeChat private chat is fine)
- Android Performance Optimization Knowledge Planet: Historical introduction; the community has closed.
One person can go faster, a group of people can go further
