Frame Rate — är antalet bildrutor som grafiksystemet visar under en sekund. I mobilappar avgör bildfrekvensen direkt hur jämna animationer, scrollning och övergångar mellan skärmar är. Enligt uppgifter från Android Developers, 2025 är mål-Frame Rate 60 fps för standarddisplayer och 120 fps för enheter med hög uppdateringsfrekvens. Avvikelse från målvärdet leder till visuella hackningar och försämrad användarupplevelse.
Huvudpunkter
Frame Rate (bildfrekvens) — är ett mått mätt i bildrutor per sekund (fps) som visar hur många gånger per sekund appen uppdaterar bilden på skärmen. Det mänskliga ögat uppfattar rörelse som jämn från 24 fps (film), men för interaktivt UI krävs minst 60 fps för att beröringar och animationer ska kännas omedelbara. Varje bildruta är en fullständig cykel: bearbetning av användarinmatning, beräkning av Layout, rendering av View-hierarkin och visning på skärmen. Om någon av faserna överskrider den tilldelade tidsbudgeten (16.6 ms vid 60 fps) hoppas bildrutan över och användaren ser en hackning.
Det är viktigt att skilja appens Frame Rate från skärmens uppdateringsfrekvens (Refresh Rate). Uppdateringsfrekvensen är en skärmegenskap: hur många gånger per sekund displayen fysiskt uppdaterar bilden (60, 90, 120 eller 144 Hz). Frame Rate — är hur många bildrutor per sekund som appen hinner rendera. Om appen producerar 60 fps på en 120 Hz-display kommer varannan bildruta att dupliceras — bilden förblir jämn, men inte lika responsiv som den skulle kunna vara. Enligt Google I/O 2023 kan moderna flaggskepp hålla 120 fps i enkla UI-scenarier, men vid tung belastning (spel, komplexa listor) sjunker frekvensen till 40–60 fps.
Rendering av en bildruta i en mobilapp går igenom en pipeline med flera steg. I Android omfattar pipelinen: bearbetning av inmatning (Input), animation (Animation), mätning och placering (Layout), ritning (Draw), synkronisering med GPU och visning på skärmen (Swap). Varje steg utförs på CPU eller GPU, och den totala tiden för alla steg får inte överskrida bildrutans budget. För 60 fps är budgeten 16.6 ms, för 120 fps — 8.3 ms. Choreographer (Android) och CADisplayLink (iOS) synkroniserar renderingen med skärmens vertikala uppdatering (VSync), vilket garanterar att en bildruta endast visas vid skärmuppdateringsögonblicket och förhindrar bildrivning (tearing).
I iOS är pipelinen liknande: Run Loop bearbetar händelser, Core Animation beräknar lager, Render Server (en separat process) renderar och skickar bildrutan till GPU:n. Skillnaden i iOS — den separata Render Server-processen som isolerar renderingen från huvudappen. Om appen blockerar huvudtråden kan Render Server fortfarande visa den senast kända bildrutan, men animationer stannar. Om Render Server själv inte hinner med — är GPU:n overksam och Frame Rate sjunker. Enligt Apple WWDC 2022 är de vanligaste orsakerna till lågt Frame Rate i iOS överdriven nästling av CALayer, tunga shadowPath och rendering utanför skärmen (offscreen rendering).
Kod i Kotlin prenumererar på Choreographer.FrameCallback och loggar den faktiska tiden mellan bildrutor. Om intervallet överstiger 16.6 ms — registreras en missad bildruta.
class FrameRateMonitor {
private var lastFrameTime = 0L
private val frameCallback =
Choreographer.FrameCallback { frameTimeNanos ->
if (lastFrameTime != 0L) {
val deltaMs = (frameTimeNanos - lastFrameTime) / 1_000_000f
if (deltaMs > 16.6f) {
Log.w("FrameRate",
"Skipped frame: $deltaMs ms")
}
}
lastFrameTime = frameTimeNanos
Choreographer.getInstance()
.postFrameCallback(this)
}
fun start() {
Choreographer.getInstance()
.postFrameCallback(frameCallback)
}
}
Refresh Rate (uppdateringsfrekvens) — är en hårdvaruegenskap hos displayen som bestämmer hur många gånger per sekund skärmen fysiskt ritar om bilden. Standarddisplayer har 60 Hz, moderna flaggskepp — 90, 120 eller 144 Hz. Appens Frame Rate kan vara lägre, lika med eller högre än uppdateringsfrekvensen (i det senare fallet kastas överskjutande bildrutor bort). Det ideala scenariot — Frame Rate sammanfaller med Refresh Rate: varje hårdvarucykel får en ny bildruta från appen och rörelsen är maximalt jämn. Om Frame Rate är lägre upprepar displayen den sista bildrutan, vilket uppfattas som mikrohackningar (stutter).
Android och iOS stöder dynamisk växling av uppdateringsfrekvens. Android 12+ använder Smart Refresh Rate: vid scrollning höjer systemet frekvensen till 120 Hz, vid statiskt innehåll sänker den till 60 Hz för att spara batteri. iOS ProMotion (iPhone 13 Pro och nyare) fungerar liknande — frekvensen varierar från 10 till 120 Hz beroende på innehållet. Utvecklaren bör kontrollera om enheten stöder hög frekvens och anpassa tidsbudgeten per bildruta. Om appen inte hinner rendera en bildruta på 8.3 ms (för 120 Hz) är det bättre att tvinga den att arbeta på 60 Hz — detta ger en stabil Frame Rate utan missade bildrutor.
| Typ av display | Refresh Rate | Budget per bildruta | Enheter |
|---|---|---|---|
| Standard | 60 Hz | 16.6 ms | De flesta Android/iOS |
| Hög | 90 Hz | 11.1 ms | OnePlus, Pixel 6+ |
| Flaggskepp | 120 Hz | 8.3 ms | iPhone Pro, Galaxy S22+ |
| Spel | 144 Hz | 6.9 ms | ROG Phone, Nubia RedMagic |
För att mäta Frame Rate i mobilappar finns både plattformarnas inbyggda verktyg och externa profilerare tillgängliga. I Android är huvudverktyget GPU Profiling (Developer Options → Profile GPU Rendering) som visar tidsskalan för varje bildruta uppdelad per fas (Draw, Prepare, Process, Execute). Mer detaljerad analys tillhandahålls av Android Studio Profiler — den registrerar en fullständig renderingsprofil med angivelse av specifika View som orsakar omritning. I iOS används Instruments med mallen Core Animation — den visar FPS, renderingstid för lager och antal renderingar utanför skärmen.
För övervakning av Frame Rate i produktion används Firebase Performance (Android) — den samlar in Frame Rate i bakgrunden och aggregerar per enhet, OS-version och session. I iOS tillhandahåller MetricKit liknande data via MXAnimatoryMetric. För spel och Flutter-appar används FrameTimingCallback (Flutter) och Unity Profiler. Det är viktigt att mäta inte genomsnittlig Frame Rate, utan percentiler: P50, P90 och P99. En app kan visa genomsnittligt 55 fps men ha P99 = 30 fps — detta innebär att 1% av tiden ser användare kraftiga hackningar och det räcker för negativa recensioner.
Exemplet i Dart visar hur man prenumererar på FrameTimingCallback i Flutter och loggar antalet missade bildrutor. Callbacken utlöses efter varje slutförd bildruta.
import 'package:flutter/scheduler.dart';
class FrameRateLogger {
int totalFrames = 0;
int missedFrames = 0;
void start() {
SchedulerBinding.instance
.addTimingsCallback(_onReportTimings);
}
void _onReportTimings(List<FrameTiming> timings) {
for (final timing in timings) {
totalFrames++;
if (timing.totalSpan()
> Duration(milliseconds: 16)) {
missedFrames++;
}
}
debugPrint("FPS: \${totalFrames - missedFrames}");
}
}
Optimering av Frame Rate börjar med att identifiera flaskhalsar i renderingspipelinen. I Layout-fasen är de främsta problemen överdriven nästling av View-hierarkin, användning av relativa Layouts (RelativeLayout med många regler) och frekventa anrop till requestLayout. Lösning — använd ConstraintLayout eller en platt hierarki, undvik nästling av mer än 5–6 nivåer. I Draw-fasen — omritning (overdraw): när en pixel ritas flera gånger per bildruta. Till exempel en vit bakgrund för Activity under ett halvgenomskinligt fragment, under vilket det finns ytterligare ett lager — varje pixel ritas tre gånger. Verktyget Debug GPU Overdraw visar problemområden med färgindikering. Det rekommenderas att hålla overdraw på nivån 2x eller lägre.
I iOS är de främsta problemen tunga cornerRadius och masksToBounds — de orsakar rendering utanför skärmen (offscreen rendering), där Core Animation skapar en tillfällig buffer, ritar i den och sedan kopierar resultatet till skärmen. Offscreen rendering är lätt att se i Instruments Core Animation: om raden Renderer är röd — finns problem. Lösning — använd UIImageView med förbeskurna bilder istället för cornerRadius, undvik groupOpacity och shouldRasterize utan absolut nödvändighet. För båda plattformarna är det kritiskt att minimera antalet anrop till invalidate() och setNeedsDisplay() — varje sådant anrop startar en fullständig omritningscykel för vyn.
Koden demonstrerar ersättning av djup nästling av RelativeLayout med en platt struktur med ConstraintLayout. Att minska nästlingsnivån från 4 till 1 förkortar Layout-tiden med 30–50%.
// Exempel: platt struktur via ConstraintLayout
class OptimizedView(context: Context) :
ConstraintLayout(context) {
private val binding =
ItemProfileBinding.inflate(
LayoutInflater.from(context)
)
fun bind(user: User) {
binding.avatar.setImageURI(user.avatarUrl)
binding.nameText.text = user.name
// binda data utan att rita om hela containern
}
}
Moderna mobilappar använder allt oftare adaptiv Frame Rate — ett system som dynamiskt anpassar målfrekvensen till det aktuella scenariot. Vid snabb scrollning kräver listan 120 fps för jämnhet, vid en statisk skärm räcker 60 fps eller till och med 30 fps för video. I Android implementeras anpassning via Choreographer.setFrameInterval (API 33+) och Window.setFrameRate. Utvecklaren kan ange önskad frekvens till systemet: setPreferredRefreshRate i SurfaceView eller setFrameRate i Window. iOS hanterar automatiskt frekvensen via ProMotion, men utvecklaren kan explicit ställa in preferredFramesPerSecond för CADisplayLink.
Dynamisk Frame Rate är särskilt viktig för spel och appar med animationer. Enligt Google sparar en sänkning av Frame Rate från 120 till 60 Hz på en statisk skärm upp till 30–40% GPU-energi. För att uppnå bästa balans mellan jämnhet och energiförbrukning rekommenderas: mät den faktiska Frame Rate i olika scenarier, ställ in mål-fps beroende på scen (spel — 60, meny — 30, video — 24) och växla lägen via Lifecycle-aware-komponenter, så att appen vid minimering inte slösar resurser på att rendera 120 fps i bakgrunden.
Kod i Swift ställer in preferredFramesPerSecond för CADisplayLink i iOS. Vid scrollning stiger frekvensen till 120 Hz, vid stopp — sjunker till 60 Hz.
class AdaptiveFrameRateManager {
private var displayLink: CADisplayLink?
func startWithHighRate() {
displayLink = CADisplayLink(
target: self,
selector: #selector(step)
)
if #available(iOS 15.0, *) {
displayLink?.preferredFrameRateRange =
CAFrameRateRange(
minimum: 60,
maximum: 120,
preferred: 120
)
}
displayLink?.add(to: .current,
forMode: .common)
}
@objc
private func step() {
// animationsuppdatering
}
}
Vanliga frågor
För mobilappar är mål-Frame Rate 60 fps (16.6 ms per bildruta). För enheter med 120 Hz-display är 120 fps önskvärt. Värden under 30 fps försämrar användarupplevelsen märkbart.
Frame Rate — hur många bildrutor per sekund appen renderar. Refresh Rate — hur många gånger per sekund displayen fysiskt uppdaterar bilden. När Frame Rate är lägre än Refresh Rate upprepar displayen den sista bildrutan.
Använd GPU Profiling i Developer Options, Android Studio Profiler eller Firebase Performance. För programmeringsmässig mätning — Choreographer.FrameCallback med beräkning av intervallet mellan bildrutor.
Overdraw — ritning av samma pixel flera gånger per bildruta. Varje extra lager ökar tiden för Draw-fasen och sänker Frame Rate. Optimal overdraw är 2x, kritisk — 4x och högre.
Vid statiskt innehåll sänker Dynamic Frame Rate frekvensen till 30–60 Hz, vilket minskar GPU-belastningen med 30–40%. Vid scrollning stiger frekvensen till 90–120 Hz för jämnhet.
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å