Jank — är en term som betecknar märkbara hackningar eller “stamning” i gränssnittsanimationer, orsakade av att enskilda bildrutor hoppas över. I mobilappar uppstår Jank när renderingstiden för en bildruta överskrider budgeten som tilldelats av displayens uppdateringsfrekvens. Enligt Android Developers, 2025 är Jank den främsta orsaken till den subjektiva känslan av “tröghet” — appen kan vara funktionellt perfekt, men användaren uppfattar den som långsam på grund av instabil FPS.
Huvudpunkter
Jank — är en term från datorgrafik som betecknar en visuell defekt där animationen rör sig ryckigt istället för mjuk glidning. I mobil utveckling mäts Jank som antalet överhoppade bildrutor (skipped frames) per tidsenhet. Om systemet inte hinner förbereda en bildruta till VSync-ögonblicket, upprepar displayen den föregående bildrutan — en paus på 16.6 ms vid 60 Hz uppstår. En överhoppad bildruta kan vara omärkbar, men en serie av 3–5 överhoppade bildrutor i rad skapar en känsla av “tröghet” på 50–80 ms, som användaren tydligt registrerar.
Jank är särskilt kritiskt för animationer som måste fungera med konstant hastighet: scrollning av listor, animationsöppning av meny, parallaxeffekter, övergångar mellan skärmar. Enligt UX-forskning från Google (2024) får en app med Jank-indikator över 3% av scrollsessioner 22% fler enstjärniga recensioner än en app med indikator under 0.5%. Verktyget Android Vitals spårar automatiskt Jank och klassificerar det efter severity: moderate, severe och critical.
Orsakerna till Jank delas in i flera kategorier. Den första — Layout Jank: orsakas av frekventa requestLayout()-anrop på grund av ändringar i View-storlekar, LayoutTransition-animationer eller dynamisk laddning av innehåll. Varje requestLayout-anrop utlöser Measure + Layout för hela View-subträdet, vilket kan ta 5–30 ms. Den andra — Draw Jank: relaterad till overdraw och användning av tunga drawable. Den tredje — Thread Jank: blockering av main thread på grund av synkrona operationer — filinläsning, arbete med databasen på huvudtråden, avkodning av Bitmap.
Den fjärde kategorin — GC Jank: garbage collection i ART/Dalvik eller Swift ARC. När många objekt samlas på hög, utlöser GC en Stop-The-World-paus på 5–15 ms. På Android uppstår GC-pauser oftast vid frekventa allokeringar i loopar: skapande av objekt i onDraw(), allokeringar i adaptrar, oanvända lambda-uttryck. Den femte — IPC Jank: interprocesskommunikation (ContentProvider, Binder) på huvudtråden. Den sjätte — Rendering Jank: långsam GPU-rendering på grund av icke-optimala shaders eller texturer av stor storlek.
| Typ av Jank | Orsak | Typisk varaktighet | Sökverktyg |
|---|---|---|---|
| Layout | requestLayout, relayout | 5–30 ms | Perfetto, Systrace |
| Draw | Overdraw, tung drawable | 3–20 ms | GPU Profiling |
| Thread | Blockering av main thread | 10–200 ms | Android Studio Profiler |
| GC | Garbage Collection | 5–15 ms | Memory Profiler |
| Rendering | GPU-belastning | 10–50 ms | GPU Tracer, Xcode GPU |
I Android börjar diagnostik av Jank med systemspårning Perfetto. Perfetto registrerar aktivitet för alla trådar, CPU, GPU och schemaläggare. En tydlig indikator på Jank är raderna Choreographer.doFrame och Choreographer.doCallbacks: om intervallet mellan två på varandra följande doFrame-anrop överstiger 16.6 ms, hoppas bildrutan över. Perfetto visar den exakta orsaken — vilket system call, lock eller GC som orsakade fördröjningen. I Android Studio Profiler finns liknande funktionalitet tillgänglig via CPU Profiler.
För automatisk upptäckt av Jank i produktion används FrameMetricsAggregator — ett API som samlar statistik per bildruta och aggregerar per session. I Android 12+ dök PerformanceHintManager upp — ett API för ledtrådar till systemet om målbildfrekvens. Om appen anger att den arbetar i ett 120 FPS-scenario, kan systemet öka CPU/GPU-frekvensen för att förhindra Jank. För enkel loggning av alla överhoppade bildrutor räcker det att prenumerera på Choreographer.FrameCallback.
Kotlin-kod prenumererar på Choreographer.FrameCallback och loggar varje överhoppad bildruta med angivelse av fördröjningens längd. Callback anropas vid varje 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",
"$skippedFrames bildrutor hoppades över")
}
}
previousFrameTime = currentTime
Choreographer.getInstance()
.postFrameCallback(this)
}
fun start() {
Choreographer.getInstance()
.postFrameCallback(callback)
}
}
I iOS utförs diagnostik av Jank via Instruments med Core Animation-mallen. Instruments visar FPS i realtid, antalet offscreen-renderingar och hit-tester. Huvudindikatorer för Jank i iOS: röda kolumner på Core Animation-tidsskalan (överskridande av bildrutebudget), hög Renderer-indikator (betyder offscreen rendering) och låg FPS. För produktionsövervakning samlar MetricKit rapporter med metriken MXAnimatoryMetric, som inkluderar genomsnittlig FPS, P50 och P95 bildrutetid.
Native diagnostik av Jank i iOS inkluderar CADisplayLink med kontroll av timestamp och targetTimestamp. Om den aktuella timestamp ligger betydligt efter targetTimestamp, har en eller flera bildrutor hoppats över. Apple rekommenderar också att använda os_signpost för anpassad profilering: placera signpost-intervall i början och slutet av bildruterendering och kontrollera i Instruments vilka intervall som överstiger 16.6 ms. I SwiftUI för Jank-diagnostik används UIView.invalidateIntrinsicContentSize — frekvent anrop av denna metod indikerar instabil Layout.
Swift-kod bestämmer överhoppade bildrutor via CADisplayLink. Om skillnaden mellan timestamp och targetTimestamp överstiger 16.6 ms — registreras Jank.
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
}
}
}
För profilering av Jank används både inbyggda OS-verktyg och tredjeparts-SDK:er. I Android är det viktigaste verktyget Perfetto (ersatte Systrace). Perfetto möjliggör inspelning av spårningar upp till 30 sekunder och analys via webbgränssnittet ui.perfetto.dev. Det visar en exakt tidslinje med aktivitet av Choreographer, renderingstrådar (RenderThread) och GPU. För detaljerad analys av GPU-problem används AGI (Android GPU Inspector), som inte bara visar bildrutetid utan även belastningen på specifika GPU-block — shaders, rasterizer, texturblock.
I iOS är motsvarigheten Instruments med mallarna Core Animation, Metal System Trace och GPU Driver. Core Animation visar FPS och bildrutetid, Metal System Trace — GPU-arbete med detalj ner till varje draw call. För profilering på riktiga enheter under belastning används Firebase Performance (samlar Screen Rendering-metrik) och Sentry(fångar stack trace vid Jank). Det nya Android 15 Performance Hint API låter utvecklaren indikera för systemet vilka bildrutor som är viktiga och få varningar från systemet när Jank närmar sig.
Kotlin-kod använder FrameMetricsAggregator för att samla bildrutestatistik per session. Efter att aggregatorn stoppats visas antalet överhoppade bildrutor.
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",
"Jank-förhållande: ${jankFrames * 100 / totalFrames}%")
}
}
Åtgärd av Jank kräver en kombination av metoder beroende på typ. För Layout Jank: ersätt djupa hierarkier med ConstraintLayout/Compose/SwiftUI, använd merge-taggar, undvik requestLayout i animationer. För Draw Jank: använd Debug GPU Overdraw för att hitta 4x+ overdraw, ersätt tunga drawable med vektor (VectorDrawable/PDF), använd hardware layers försiktigt — de accelererar rendering men förbrukar mer GPU-minne. För Thread Jank: flytta alla I/O-operationer, databasarbete och Bitmap-avkodning till bakgrundstrådar, använd Kotlin Coroutines med rätt Dispatcher eller RxJava med Schedulers.io().
För GC Jank: minimera allokeringar i onDraw() och getView(), använd objektpooler (ObjectPool), ersätt for-each med indexerad for, använd immutable data class i Kotlin med copy() försiktigt — copy skapar ett nytt objekt. För IPC Jank: lat initialisering av ContentProvider via App Startup, flytta Binder-anrop till bakgrundstråd. För Rendering Jank: minska texturstorleken till skärmens maximala upplösning, använd ASTC eller ETC2-komprimering, undvik onödig shader compilation (kompilera shaders i förväg). Komplett lösning — regelbunden Perfetto/Instruments-profilering i CI och spårning av Jank-regressioner.
Kotlin-kod demonstrerar asynkron laddning av data på skärmen efter reportFullyDrawn, så att tungt arbete inte blockerar den första bildrutan. Callback anropas efter att användaren ser gränssnittet.
class JankSafeLoader {
suspend fun loadAfterFirstFrame(
activity: Activity
) {
// garanterar att den första bildrutan redan är renderad
if (Build.VERSION.SDK_INT >= 29) {
activity.reportFullyDrawn()
}
// tung laddning — efter första bildrutan
withContext(Dispatchers.IO) {
val data = fetchHeavyData()
withContext(Dispatchers.Main) {
updateUI(data)
}
}
}
}
Vanliga frågor
Jank — är överhoppade renderingsbildrutor som visar sig som märkbara hackningar eller ryck i animering. Uppstår när förberedelsetiden för en bildruta överskrider tidsbudgeten (16.6 ms för 60 FPS).
Layout Jank (frekventa requestLayout), Draw Jank (overdraw), Thread Jank (blockering av main thread), GC Jank (garbage collection), IPC Jank (Binder-anrop) och Rendering Jank (tunga shaders).
Använd Perfetto för systemspårning, GPU Profiling för analys av bildrutefaser och FrameMetricsAggregator för produktionsövervakning. I Android Studio — CPU Profiler med Deep Java Trace.
Via Instruments med Core Animation- eller Metal System Trace-mallen. För produktion — MetricKit med MXAnimatoryMetric. Programmatiskt — CADisplayLink med kontroll av skillnaden mellan timestamp och targetTimestamp.
Enligt Google leder en Jank-indikator över 3% av scrollsessioner (3 av 100 scrollningar innehåller hackning) till en ökning av negativa recensioner med 22%. Målindikator — mindre än 0.5% av scrollsessioner.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också