Jank — je termín označující znatelné záseky nebo „zadrhávání” animace rozhraní, způsobené vynecháním jednotlivých snímků. V mobilních aplikacích Jank vzniká, když doba renderování snímku překročí rozpočet přidělený obnovovací frekvencí displeje. Podle Android Developers, 2025 je Jank hlavní příčinou subjektivního pocitu „zpomalení” — aplikace může být funkčně perfektní, ale uživatel ji vnímá jako pomalou kvůli nestabilnímu FPS.
Hlavní body
Jank — je termín z oblasti počítačové grafiky označující vizuální vadu, při které se animace pohybuje trhaně místo plynulého klouzání. V mobilním vývoji se Jank měří jako počet vynechaných snímků (skipped frames) za jednotku času. Pokud systém nestihne připravit snímek do okamžiku VSync, displej zopakuje předchozí snímek — vzniká pauza 16.6 ms při 60 Hz. Jeden vynechaný snímek může být nepostřehnutelný, ale série 3–5 po sobě jdoucích vynechaných snímků vytváří pocit „zpomalení” trvající 50–80 ms, který uživatel jasně registruje.
Jank je obzvláště kritický pro animace, které musí pracovat konstantní rychlostí: rolování seznamu, animace otevírání menu, paralaxní efekty, přechody mezi obrazovkami. Podle UX výzkumu Google (2024) získává aplikace s ukazatelem Jank vyšším než 3% rolování o 22% více jednohvězdičkových recenzí než aplikace s ukazatelem nižším než 0.5%. Nástroj Android Vitals automaticky sleduje Jank a klasifikuje jej podle severity: moderate, severe a critical.
Příčiny Jank se dělí do několika kategorií. První — Layout Jank: způsobený častými requestLayout() kvůli změnám velikostí View, animacím LayoutTransition nebo dynamickému načítání obsahu. Každé volání requestLayout spouští Measure + Layout pro celý podstrom View, což může trvat 5–30 ms. Druhá — Draw Jank: související s overdraw a používáním těžkých drawable. Třetí — Thread Jank: blokování main thread kvůli synchronním operacím — načítání souborů, práce s databází na hlavním vlákně, dekódování Bitmap.
Čtvrtá kategorie — GC Jank: garbage collection v ART/Dalvik nebo Swift ARC. Když se na hromadě nahromadí mnoho objektů, GC spustí Stop-The-World pauzu trvající 5–15 ms. Na Androidu se GC pauzy nejčastěji vyskytují při častých alokacích v cyklech: vytváření objektů v onDraw(), alokace v adaptérech, nepoužívané lambda výrazy. Pátá — IPC Jank: meziprocesová komunikace (ContentProvider, Binder) na hlavním vlákně. Šestá — Rendering Jank: pomalé renderování GPU kvůli neoptimálním shaderům nebo texturám velké velikosti.
| Typ Jank | Příčina | Typická doba trvání | Nástroj vyhledávání |
|---|---|---|---|
| Layout | requestLayout, relayout | 5–30 ms | Perfetto, Systrace |
| Draw | Overdraw, těžké drawable | 3–20 ms | GPU Profiling |
| Thread | Blokování main thread | 10–200 ms | Android Studio Profiler |
| GC | Garbage Collection | 5–15 ms | Memory Profiler |
| Rendering | Zatížení GPU | 10–50 ms | GPU Tracer, Xcode GPU |
V Androidu začíná diagnostika Jank systémovým trasováním Perfetto. Perfetto zaznamenává aktivitu všech vláken, CPU, GPU a plánovače. Jasným indikátorem Jank jsou řádky Choreographer.doFrame a Choreographer.doCallbacks: pokud interval mezi dvěma po sobě jdoucími voláními doFrame přesáhne 16.6 ms, snímek je vynechán. Perfetto ukazuje přesnou příčinu — který system call, lock nebo GC způsobil zpoždění. V Android Studio Profiler je podobná funkcionalita dostupná přes CPU Profiler.
Pro automatickou detekci Jank v produkci se používá FrameMetricsAggregator — API, které sbírá statistiku pro každý snímek a agreguje za relaci. V Android 12+ se objevil PerformanceHintManager — API pro nápovědy systému o cílové snímkové frekvenci. Pokud aplikace indikuje, že pracuje ve scénáři 120 FPS, systém může zvýšit frekvenci CPU/GPU pro prevenci Jank. Pro jednoduché logování všech vynechaných snímků stačí se přihlásit k odběru Choreographer.FrameCallback.
Kód v Kotlin se přihlásí k odběru Choreographer.FrameCallback a loguje každý vynechaný snímek s uvedením doby zpoždění. Callback je volán při každém VSync.
class JankDetector {
private val frameBudget = 16_666_666L
private var previousFrameTime = 0L
private val callback =
Choreographer.FrameCallback { currentTime ->
if (previousFrameTime != 0L) {
val frameDuration =
currentTime - previousFrameTime
val skippedFrames =
(frameDuration / frameBudget) - 1
if (skippedFrames > 0) {
Log.w("Jank",
"Vynecháno $skippedFrames snímků")
}
}
previousFrameTime = currentTime
Choreographer.getInstance()
.postFrameCallback(this)
}
fun start() {
Choreographer.getInstance()
.postFrameCallback(callback)
}
}
V iOS se diagnostika Jank provádí přes Instruments se šablonou Core Animation. Instruments zobrazuje FPS v reálném čase, počet offscreen-renderů a hit-testy. Hlavní indikátory Jank v iOS: červené sloupce na časové ose Core Animation (překročení rozpočtu snímku), vysoký ukazatel Renderer (znamená offscreen rendering) a nízké FPS. Pro monitorování produkce MetricKit sbírá reporty s metrikou MXAnimatoryMetric, která zahrnuje průměrné FPS, P50 a P95 dobu snímku.
Nativní diagnostika Jank v iOS zahrnuje CADisplayLink s kontrolou timestamp a targetTimestamp. Pokud aktuální timestamp výrazně zaostává za targetTimestamp, byl vynechán jeden nebo více snímků. Apple také doporučuje používat os_signpost pro vlastní profilování: umístit signpost-interval na začátek a konec renderování snímku a v Instruments zkontrolovat, které intervaly přesahují 16.6 ms. Ve SwiftUI se pro diagnostiku Jank používá UIView.invalidateIntrinsicContentSize — časté volání této metody svědčí o nestabilním Layout.
Kód ve Swift určuje vynechané snímky přes CADisplayLink. Pokud rozdíl mezi timestamp a targetTimestamp přesáhne 16.6 ms — Jank je zaznamenán.
class JankMonitor {
private var displayLink: CADisplayLink?
private var totalJank = 0
func start() {
displayLink = CADisplayLink(
target: self,
selector: #selector(detectJank)
)
displayLink?.add(to: .current,
forMode: .common)
}
@objc
private func detectJank() {
guard let link = displayLink else { return }
let delay = link.targetTimestamp
- link.timestamp
if delay > 0.0167 {
totalJank += 1
}
}
}
Pro profilování Jank se používají jak vestavěné nástroje OS, tak SDK třetích stran. V Androidu je klíčovým nástrojem Perfetto (nahradil Systrace). Perfetto umožňuje nahrávat trasování až 30 sekund a analyzovat je přes webové rozhraní ui.perfetto.dev. Zobrazuje přesnou časovou osu s prací Choreographer, vláken renderování (RenderThread) a GPU. Pro detailní analýzu problémů s GPU se používá AGI (Android GPU Inspector), který ukazuje nejen dobu snímku, ale také zatížení konkrétních bloků GPU — shaderů, rasterizátoru, bloku textur.
V iOS je obdobou Instruments se šablonami Core Animation, Metal System Trace a GPU Driver. Core Animation zobrazuje FPS a dobu snímku, Metal System Trace — práci GPU s detailem až na každý draw call. Pro profilování na reálných zařízeních pod zátěží se používají Firebase Performance (sbírá metriku Screen Rendering) a Sentry (zachycuje stack trace při Jank). Nové API Android 15 Performance Hint umožňuje vývojáři indikovat systému, které snímky jsou důležité, a dostávat od systému varování při přiblížení k Jank.
Kód v Kotlin používá FrameMetricsAggregator pro sběr statistik snímků za relaci. Po zastavení agregátoru se zobrazí počet vynechaných snímků.
class JankAggregator(private val activity: Activity) {
private val aggregator = FrameMetricsAggregator()
fun startCollection() {
aggregator.add(activity.window)
}
fun stopAndReport() {
aggregator.remove()
val result = aggregator.getMetrics()
val totalFrames = result
?.get(FrameMetrics.TOTAL_DURATION)
?.size ?: 0
val jankFrames = result
?.get(FrameMetrics.TOTAL_DURATION)
?.count { it > 16_666_666L} ?: 0
Log.d("JankReport",
"Poměr Jank: ${jankFrames * 100 / totalFrames}%")
}
}
Odstranění Jank vyžaduje kombinaci metod v závislosti na jeho typu. Pro Layout Jank: nahrazení hlubokých hierarchií ConstraintLayout/Compose/SwiftUI, použití merge-tagů, vyhýbání se requestLayout v animacích. Pro Draw Jank: použití Debug GPU Overdraw k nalezení 4x+ overdraw, nahrazení těžkých drawable vektorovými (VectorDrawable/PDF), opatrné použití hardware layers — urychlují renderování, ale spotřebovávají více GPU paměti. Pro Thread Jank: přesun všech I/O operací, práce s databází a dekódování Bitmap na vlákna na pozadí, použití Kotlin Coroutines se správným Dispatcher nebo RxJava s Schedulers.io().
Pro GC Jank: minimalizace alokací v onDraw() a getView(), použití objektových poolů (ObjectPool), nahrazení for-each indexovaným for, opatrné použití immutable data class v Kotlin s copy() — copy vytváří nový objekt. Pro IPC Jank: líná inicializace ContentProvider přes App Startup, přesun Binder volání na vlákno na pozadí. Pro Rendering Jank: snížení velikosti textur na maximální rozlišení obrazovky, použití komprese ASTC nebo ETC2, vyhýbání se zbytečné shader compilation (kompilace shaderů předem). Komplexní řešení — pravidelné spouštění profilování Perfetto/Instruments v CI a sledování Jank regresí.
Kód v Kotlin demonstruje asynchronní načítání dat na obrazovku po reportFullyDrawn, aby těžká práce neblokovala první snímek. Callback je volán poté, co uživatel vidí rozhraní.
class JankSafeLoader {
suspend fun loadAfterFirstFrame(
activity: Activity
) {
// garantujeme, že první snímek je již vykreslen
if (Build.VERSION.SDK_INT >= 29) {
activity.reportFullyDrawn()
}
// těžké načítání — po prvním snímku
withContext(Dispatchers.IO) {
val data = fetchHeavyData()
withContext(Dispatchers.Main) {
updateUI(data)
}
}
}
}
Často kladené otázky
Jank — jsou vynechané renderovací snímky, které se projevují jako znatelné záseky nebo trhnutí animace. Vzniká, když doba přípravy snímku překročí časový rozpočet (16.6 ms pro 60 FPS).
Layout Jank (časté requestLayout), Draw Jank (overdraw), Thread Jank (blokování main thread), GC Jank (garbage collection), IPC Jank (Binder volání) a Rendering Jank (těžké shadery).
Použijte Perfetto pro systémové trasování, GPU Profiling pro analýzu fází snímku a FrameMetricsAggregator pro monitorování produkce. V Android Studio — CPU Profiler s Deep Java Trace.
Přes Instruments se šablonou Core Animation nebo Metal System Trace. Pro produkci — MetricKit s MXAnimatoryMetric. Programově — CADisplayLink s kontrolou rozdílu timestamp a targetTimestamp.
Podle Google, ukazatel Jank vyšší než 3% relací rolování (3 ze 100 rolování obsahuje zásek) vede k nárůstu negativních recenzí o 22%. Cílový ukazatel — méně než 0.5% relací rolování.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také