🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🔧 AI Nachrichten ChatGPT automatically logged out [Fix](12.09.2026 um 17:09 Uhr)
⚠️ Malware / Trojaner / VirenWindows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC(10.09.2026 um 20:11 Uhr)
🪟 Windows TippsServertimeout in Outlook über 10 Minuten verlängern(12.09.2026 um 15:10 Uhr)
🔧 AI Nachrichten Stealing AI Reasoning Traces(08.09.2026 um 12:20 Uhr)
🔧 AI Nachrichten AIs as Modern Genies(08.09.2026 um 19:12 Uhr)
🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🔧 AI Nachrichten ChatGPT automatically logged out [Fix](12.09.2026 um 17:09 Uhr)
⚠️ Malware / Trojaner / VirenWindows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC(10.09.2026 um 20:11 Uhr)
🪟 Windows TippsServertimeout in Outlook über 10 Minuten verlängern(12.09.2026 um 15:10 Uhr)
🔧 AI Nachrichten Stealing AI Reasoning Traces(08.09.2026 um 12:20 Uhr)
🔧 AI Nachrichten AIs as Modern Genies(08.09.2026 um 19:12 Uhr)

🔧 Programmierung 🕛 vor 3 Monaten 4 Min Lesezeit
0

Fixing Android Jank You Can't See

↗ Quelle (dev.to)
🗣️ Stimme:

CODE
---
title: "Fixing Android Jank You Can't See with Systrace and Perfetto"
published: true
description: "A hands-on guide to finding hidden jank in Jetpack Compose apps RenderThread stalls, recomposition traps, and frame timing fixes that keep 99th-percentile frames under 16ms."
tags: android, kotlin, performance, architecture
canonical_url: https://blog.mvpfactory.co/fixing-android-jank-you-cant-see-with-systrace
---

## What We Will Build

By the end of this workshop, you will have a repeatable profiling workflow that catches the jank your users feel but Android Studio's basic profiler never shows. We will trace the full frame lifecycle in Perfetto, identify RenderThread GPU fence stalls, fix Compose recomposition traps, and tune lazy layout prefetch — the patterns that keep 99th-percentile frame times under 16ms.

## Prerequisites

-
Android Studio Hedgehog or later
- A Jetpack Compose project with a scrollable `LazyColumn`
- Perfetto UI ([ui.perfetto.dev](https://ui.perfetto.dev)) or command-line `systrace`
- Compose Compiler plugin configured in your Gradle build

## Step 1: Understand the Frame Lifecycle

Here is the pipeline that matters. The docs do not mention this, but your main thread can finish in 4ms and you still drop the frame if the RenderThread stalls past the vsync boundary.

| Stage | Thread | Jank Risk |
|---|---|---|
| `doFrame` | Main | Recomposition storms |
| `syncFrameState` | Main → Render | Large draw ops |
| `issueDrawCommands` | RenderThread | Shader compilation |
| `GPU Completion Fence` | RenderThread | Texture uploads, overdraw |
| `

`
| SurfaceFlinger | Missed vsync deadline |

Perfetto shows RenderThread stalls as a gap between `DrawFrame` end and the next `doFrame` start on the RenderThread track. That is where your invisible jank lives.

## Step 2: Catch RenderThread Stalls

The pattern I use in every project to catch first-frame shader compilation: filter your Perfetto trace on the RenderThread track and look for `GrGLGpu::compile` slices. These show up as single 40-80ms spikes that are completely invisible in CPU profiling. They happen once, look like noise, and teams ignore them. Don't.

Pre-warm shader paths during splash screen transitions and keep your `RenderNode` tree flat. Every nested `graphicsLayer` creates a new RenderNode with its own display list.

## Step 3: Fix Compose Recomposition Traps

Two patterns dominate. Let me show you both.

**Unstable lambda captures** — here is the gotcha that will save you hours:







kotlin

// BAD: New lambda instance every recomposition

@Composable

fun ItemRow(item: Item, viewModel: MyViewModel) {

Button(onClick = { viewModel.onItemClick(item.id) }) {

Text(item.name)

}

}



// GOOD: Stable reference via remember

@Composable

fun ItemRow(item: Item, viewModel: MyViewModel) {

val onClick = remember(item.id) { { viewModel.onItemClick(item.id) } }

Button(onClick = onClick) {

Text(item.name)

}

}




CODE

In a `LazyColumn` with 50 visible items, unstable lambdas trigger 50 unnecessary recompositions per frame during scroll.

**Missing stability annotations** — classes from external modules are assumed unstable by default:







kotlin

@Immutable

data class UiItem(val id: String, val title: String, val imageUrl: String)




CODE

Run the Compose Compiler metrics report (`-P plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=...`) to audit unstable classes. I have seen scroll jank drop by 60% after stabilizing just three key model classes. Three classes. That is it.

## Step 4: Tune Lazy Layout Prefetch

Here is the minimal setup to get this working. The default prefetch distance works for simple items, but complex compositions blow past the frame budget.

| Strategy | Budget Used | Result |
|---|---|---|
| Default (next item) | ~4-6ms | Good for simple items |
| `LazyLayoutPrefetchStrategy(3)` | ~2ms × 3 spread across frames | Better for complex items |
| Over-prefetch (10+) | Steals from visible frames | Worse, causes visible jank |

The sweet spot is 2-3 items with compositions completing within 3ms each. Measure in Perfetto by filtering to `compose:recompose` slices during scroll.

## Step 5: Batch Display List Operations







kotlin

// BAD: Three separate draw passes

Modifier

.drawBehind { drawRect(backgroundColor) }

.drawBehind { drawRoundRect(borderColor, cornerRadius = 8.dp.toPx()) }

.drawBehind { drawCircle(accentColor, radius = 4.dp.toPx()) }



// GOOD: Single draw pass

Modifier.drawBehind {

drawRect(backgroundColor)

drawRoundRect(borderColor, cornerRadius = 8.dp.toPx())

drawCircle(accentColor, radius = 4.dp.toPx())

}




CODE

This reduces RenderNode operations and keeps display list serialization under budget.

## Gotchas

- **Average frame time lies.** Your users feel the 99th percentile. A single 34ms frame in a 300-frame scroll ruins perceived smoothness. Stop looking at averages.
- **First-frame shader compilation is silent.** It shows up once as a 40-80ms RenderThread spike and looks like noise. Pre-warm during splash transitions.
- **Nested `graphicsLayer` calls are expensive.** Each one creates a new RenderNode with its own display list. Flatten aggressively.
- **GPU completion fence stalls over 8ms are your signal.** Filter the Perfetto RenderThread track — that is exactly where invisible jank hides.

## Conclusion

Profile with Perfetto, not just Android Studio. Run the Compose Compiler stability report on every release. Annotate key UI model classes with `@Immutable` or `@Stable`, wrap lambdas in `remember` with proper keys, tune `LazyColumn` prefetch to 2-3 items, and batch draw operations into single modifiers. Your 299 fast frames do not matter if frame 300 stutters.


Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
The Gemini desktop app is now available for Windows
1 Quelle
ChatGPT automatically logged out [Fix]
1 Quelle
Windows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Fixing Android Jank You Can't See

Thematisch verwandte Begriffe: Fixing, Android, Jank, Cant · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...