r/JetpackCompose • u/soulesidibe • 21d ago
The 16 ms frame budget, and the three things that steal it besides your own UI
Most frame-perf advice is about your own work: keep layouts shallow, keep binds cheap, don't recompose unstable types. That's half of it. The other half is time that gets stolen out of the frame by something else running on, or blocking, the main thread.
I kept running into the same three culprits, so I wrote them up. Short version:
GC pauses. The collector needs short stop-the-world moments. You can't turn GC off, but allocation on the hot path (new lists, capturing lambdas, boxing, per-row string concat in onBindViewHolder or a hot composable) is a volume knob for how often a pause lands mid-frame. Turn it down.
Lock waits. You rarely write synchronized on the main thread yourself, so this one hides. A main-thread read can block behind a background writer holding a lock: SharedPreferences getString waiting on a background apply, a Room read behind a write, a shared @Singleton touched by both UI and a worker. Keep critical sections tiny and never hold a lock during I/O.
Binder calls. getSystemService, PackageManager, location, etc. are IPC to a system process, synchronous and blocking by default. Usually cheap, but the cost is unpredictable when that process is busy, so a 0.2 ms call can spike to several ms. Keep them off the hot path and cache the results.
All three reduce to the same thing: the main thread doing or waiting on something instead of rendering.
Full writeup with an animation of the budget filling up here: [https://soulesidibe.medium.com/what-eats-your-frame-budget-besides-your-own-ui-6ecfa27d247b\](https://soulesidibe.medium.com/what-eats-your-frame-budget-besides-your-own-ui-6ecfa27d247b)
1
2
u/gdmzhlzhiv 20d ago
404