Skip to content
timkrestPublic

Latest commit

 

History

141 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FrameHUD

Maven Central Build License API 24+

English · Русский

A debug panel over your running app that breaks every frame into its stages and names the one that is slowing you down.

The panel over the sample app while scrolling a list

JankStats hands you a callback per frame. FrameHUD is what you would build on top of it: the panel, statistics per screen and session, and a jank gate for tests. How it compares with Macrobenchmark, Perfetto and Play Vitals.

  • A row per stage. input, anim, layout, draw on the main thread, then sync, command, swap on the render thread, and gpu
  • Says why frames drop. Thermal throttling, GC pauses, too few Choreographer ticks, or a late start
  • Keeps the case, not just the number. A jank burst or a frozen frame is saved with the frames around it and the readings of that moment, and with the stack the main thread stood in when it was the one stuck
  • Knows how the last run ended. An ANR, a crash or memory reclaimed, with the screen it happened on and, for an ANR, where the main thread stood
  • Names screens for you. After the activity, the fragment, or the Navigation destination on screen
  • Measures your app, not itself. The panel draws in its own window
  • Fails tests on jank. A JUnit rule with thresholds
  • Compares with earlier runs. A baseline per device, so a gate checks the delta instead of a fixed number
  • Works without the panel. framehud-metrics collects and reports with no window and no permission
  • Made for QA runs. adb commands, JSON and HTML session exports, sections and counters in a system trace
  • Nothing in release builds. debugImplementation leaves out the panel, its provider and the SYSTEM_ALERT_WINDOW it declares

Quick start

dependencies {
    debugImplementation("com.timkrest:framehud:0.20.0")
}

There is nothing to call. A ContentProvider starts the panel, and it follows whichever activity has focus.

Requires minSdk 24. Frame phases come from FrameMetrics; GPU timings need API 31+ and a driver that reports them.

debugImplementation already keeps everything out of a release build. Add framehud-noop if you call FrameHud outside src/debug, or use framehud-compose, which calls it for you. A release build has to compile and run those calls, and without noop it has no FrameHud class at all. Noop mirrors the API with empty bodies:

releaseImplementation("com.timkrest:framehud-noop:0.20.0")

Modules

Artifact What it is Add it as
framehud The panel, with everything below it and the adb commands debugImplementation
framehud-metrics Collection, events and exports, with no window a QA flavour, e.g. qaImplementation
framehud-qa The adb commands for a build on framehud-metrics next to framehud-metrics
framehud-compose MarkWhileScrolling and CountCompositions implementation, with framehud-noop in release
framehud-instrumentation The jank gate and FrameHudResetRule androidTestImplementation
framehud-noop The same API with empty bodies releaseImplementation

What the panel shows

ui 59/s · 16.7ms   58 FPS
⚠ layout 8.4 ms
CPU        now   avg  peak
input      0.1   0.2   1.1
anim       0.3   0.4   2.0
layout     7.9   8.4  22.3 ◀
draw       1.2   1.4   6.7
RENDER
sync       0.4   0.5   1.9
command    0.6   0.7   3.1
swap       0.2   0.3   1.4
GPU
gpu        2.1   2.4   9.8
delay      0.3   0.4   2.2
other      0.3   0.3
TOTAL     11.3  12.6  38.6
over      -5.4  -4.1  21.9
pipe:cpu   9.5  10.4
win  jank  7.5%  p95  18.4  max  22.3
ses  p50  11.8  p95  19.6  p99  28.4
ses 4312f 1m12s jank 6.4% frz0 run3
lost 2.1s
mem 84/256 ▲96 · nat 37 ▲41 MB
gc x3 · 18 ms
therm none · hr 0.68
cpu 62% ▲140 · pss 214 ▲240 MB
thr 38 ▲44 · fd 210 ▲260

The header shows the main thread's Choreographer tick rate, the frame budget and FPS. The verdict under it names the row to look at, and ◀ marks that row. Columns read now avg peak: the current frame, the average over the window, and the peak since the last reset. A tap steps through the three views — every row, the frame rows on their own, and one line; holding freezes the readings, and ▤ switches to the worst screens and back.

Reading the panel explains every row and what to do when one turns red.

Fail tests on jank

androidTestImplementation("com.timkrest:framehud-instrumentation:0.20.0")
@get:Rule val noJank = DetectJankAfterTestSuccess(JankThresholds(maxJankPercent = 2f))

Thresholds can be fixed, as above, or relative to a baseline of earlier runs on the same device.

Sample

./gradlew :sample:installDebug

Load stresses the frame pipeline with seven toggles, Readouts shows every reading taken from the flows instead of the panel, and Session is what a QA run ends with: baselines, past runs, incidents, export.

Documentation

  • Guide: collection without the panel, configuration, events and incidents, the Perfetto flight recorder, screens and marks, exports and adb, baselines and the jank gate, and what to check when something looks wrong
  • Reading the panel: what every row means, how to measure a screen, and what to do when something turns red
  • Comparing the tools: how FrameHUD differs from JankStats, Macrobenchmark, Perfetto and Play Vitals
  • How to tell which stage of the frame is making your app janky: two rendering bugs that a frame counter reports as the same, measured on a Galaxy S25 Ultra
  • API reference: generated from the sources of each release
  • Changelog: what changed in each release
  • Contributing: how to build, and what to check before opening a pull request. Contributions are covered by a CLA, which a bot will ask you to sign.

License

Apache 2.0. See LICENSE and NOTICE.

Releases

Packages

Contributors

Languages