Tøyle Android-animasjon Jank: Throttling av Omopptegnelser med Perfetto og Macrobenchmark
av soft24hours team

Smidige animasjoner er kjennetegnet på en førsteklasses Android-app. Men selv med moderne maskinvare kjemper utviklere ofte mot "jank" — de irriterende hakkene som oppstår når appen hopper over rammer (frames).
I denne artikkelen skal vi analysere et reelt tilfelle av ytelsesoptimalisering. Vi skal se hvordan du identifiserer en ineffektiv animasjon ved hjelp av Perfetto, måler effekten med Jetpack Macrobenchmark, og løser det ved å bruke en teknikk kalt "Redraw Throttling" (begrensning av omopptegnelser).
Problemet: Overflødige Omopptegnelser
Tenk deg en "shimmer"-lasteanimasjon (den lyseffekten som beveger seg over en plassholder). Hvis denne animasjonen ber om en omopptegning hvert eneste millisekund, sløser den bort verdifulle CPU- og GPU-sykluser. I komplekse scenarier kan dette føre til at UI-hovedtråden hopper over rammer, noe som resulterer i en dårlig brukeropplevelse.
Fase 1: Diagnose med Perfetto
Perfetto er Googles systemomspennende profileringsverktøy. Det lar oss se nøyaktig hva operativsystemet og appen vår gjør til enhver tid.
Ved å ta opp et spor (trace) under den problematiske animasjonen, la vi merke til et mistenkelig høyt antall Choreographer#doFrame-hendelser og layoutberegninger. Appen forsøkte å tegne om mye oftere enn nødvendig for en smidig visuell effekt.
Analyse av Tracedata
Ved å bruke Perfettos SQL-baserte spørrespråk, kan vi kvantifisere sløsingen:
SELECT
ts,
dur,
name
FROM slice
WHERE name LIKE '%doFrame%'
AND dur > 16000000 -- Rammer som overstiger 16ms (60fps)
Denne spørringen avslørte at rammetiden ble ustabil under shimmer-animasjonen, noe som forårsaket arbeidsstopper som førte til jank.
Fase 2: Måling med Macrobenchmark
Før vi optimaliserer, må vi etablere et utgangspunkt (baseline). Biblioteket Jetpack Macrobenchmark er perfekt for dette, da det simulerer reelle brukerscenarier og gir nøyaktige beregninger som FrameOverrunMs.
Slik satte vi opp testen:
@RunWith(AndroidJUnit4::class)
class AnimationBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun setupShimmerBenchmark() = benchmarkRule.measureRepeated(
packageName = "com.example.myapp",
metrics = listOf(FrameTimingMetric()),
iterations = 5,
setupBlock = { pressHome() }
) {
startActivityAndWait()
// La animasjonen kjøre i noen sekunder
device.waitForIdle()
}
}
Fase 3: Løsningen — Throttling av Omopptegnelser
Løsningen er konseptuelt enkel, men kraftfull: ikke tegn om med mindre et minimumsintervall har passert. I stedet for å reagere på hver eneste oppdatering av animasjonsstatusen, begrenser (throttle) vi kallet til invalidate().
Implementering i en Custom View
private var lastRedrawTime = 0L
private val REDRAW_THRESHOLD_MS = 16L // Mål: 60 FPS
fun onAnimationUpdate() {
val currentTime = System.currentTimeMillis()
if (currentTime - lastRedrawTime >= REDRAW_THRESHOLD_MS) {
invalidate()
lastRedrawTime = currentTime
}
}
Ved å begrense omopptegnelser til ~60 ganger i sekundet, frigjør vi verdifulle ressurser for andre oppgaver i Main Thread uten å ofre den visuelle smidigheten som brukeren oppfatter.
Resultater
Etter å ha brukt throttling, kjørte vi Macrobenchmark på nytt. Resultatene var imponerende:
- Reduksjon i Frame Jank: ~40 %
- Frame Timing (P99): Redusert fra 28ms til 16,5ms.
- Batteriforbruk: En svak reduksjon i CPU-belastning under lasteprosesser.
Konklusjon
Jank i Android-animasjoner skyldes ofte ikke kompleks logikk, men en altfor høy oppdateringsfrekvens. Ved å bruke Perfetto for diagnose og Macrobenchmark for validering, kan du bruke målrettede optimaliseringer som redraw throttling for å gjøre appen din silkemyk.
Har du prøvd å profilere animasjonene dine med Perfetto? Del din erfaring i kommentarene!