FPS (Frames Per Second) — este o metrică care arată câte cadre individuale sistemul grafic redă într-o secundă. În dezvoltarea mobilă, FPS este un indicator standard al performanței UI: cu cât FPS este mai mare, cu atât animațiile sunt mai fluide și interfața mai receptivă. Conform datelor Google Android Performance, 2025, valoarea țintă a FPS pentru aplicațiile mobile este de 60 de cadre pe secundă — acesta este pragul la care ochiul uman percepe mișcarea ca fiind continuă și lină.
Puncte cheie
FPS (Frames Per Second) — este o unitate de măsură a ratei de cadre utilizată în grafica pe calculator, video și interfețele mobile. Fiecare cadru este o imagine statică afișată pe ecran pentru o perioadă scurtă de timp. La schimbarea rapidă a cadrelor, creierul le percepe ca mișcare continuă — acest efect se numește persistența vederii. Pentru aplicațiile mobile, FPS este o metrică critică, deoarece orice cadru pierdut (drop) transformă o animație lină într-un blocaj vizibil. Aplicația trebuie să reușească să redea fiecare cadru strict în limita bugetului de timp: 16.6 ms pentru 60 FPS, 11.1 ms pentru 90 FPS, 8.3 ms pentru 120 FPS.
FPS se măsoară nu numai pentru UI, ci și pentru jocuri, video și cameră. În jocuri, FPS depinde de complexitatea scenei, calitatea texturilor și puterea GPU. În video, FPS este fix (24, 30, 60 cadre/s) și este determinat de conținut. În aplicațiile mobile, FPS depinde de eficiența codului UI: complexitatea Layout, numărul de View-uri, frecvența redesenerii și funcționarea GC (Garbage Collection). Conform Apple WWDC 2022, FPS mediu într-o aplicație poate scădea cu 10–15% din cauza actualizării ineficiente a colecțiilor (reloadData în loc de insert/delete/dequeueReusableCell). Măsurarea FPS în timp real este o practică standard pentru inginerii QA și dezvoltatorii care lucrează la performanță.
Calculul FPS într-o aplicație mobilă se bazează pe măsurarea timpului între cadrele consecutive. Cea mai simplă formulă: FPS = 1000 / deltaTimeMs, unde deltaTimeMs este intervalul dintre finalizarea cadrului anterior și finalizarea celui curent. Dacă cadrul curent a fost redat în 20 ms, FPS = 1000 / 20 = 50. Cu toate acestea, în practică, FPS este rareori stabil chiar și într-o singură secundă: un profil tipic include cadre de 12–16 ms intercalate cu cadre pierdute (jank) sau lente (40–60 ms). De aceea, FPS se măsoară ca medie mobilă pe 1–5 secunde sau ca percentile ale distribuției timpilor de cadru.
În Android, FPS se calculează prin Choreographer, care primește un callback de la VSync (impulsul de sincronizare a ecranului). Fiecare callback corespunde unui cadru. Dacă callback-ul nu a sosit — cadrul a fost pierdut. Choreographer permite măsurarea exactă a numărului de cadre pe secundă și a numărului de cadre pierdute (skipped frames). În iOS, CADisplayLink funcționează similar — este apelat de fiecare dată când ecranul este gata să redea un nou cadru. Proprietatea timestamp conține timpul exact al ultimului cadru, iar targetTimestamp — timpul așteptat al următorului. Diferența dintre ele este bugetul de timp pentru cadrul curent.
Codul în Swift demonstrează o monitorizare simplă a FPS prin CADisplayLink. Contorul frameCount se incrementează la fiecare apel și o dată pe secundă se calculează FPS-ul real.
class FpsCounter {
private var displayLink: CADisplayLink?
private var frameCount = 0
private var lastTime = TimeInterval(0)
func start() {
displayLink = CADisplayLink(
target: self,
selector: #selector(countFrame)
)
displayLink?.add(to: .current,
forMode: .common)
}
@objc
private func countFrame() {
frameCount += 1
let now = Date().timeIntervalSince1970
if now - lastTime >= 1.0 {
print("FPS: \(frameCount)")
frameCount = 0
lastTime = now
}
}
}
Standardul 60 FPS (60 Hz) s-a consolidat în industrie din mai multe motive. Primul — fiziologic: ochiul uman nu distinge cadrele individuale la o frecvență de peste 50–60 Hz, percepându-le ca mișcare lină. Acest prag se numește Critical Flicker Fusion (CFF). Al doilea — istoric: primele tuburi catodice (CRT) funcționau la 60 Hz în SUA (NTSC) și 50 Hz în Europa (PAL). Ecranele LCD moderne au moștenit această frecvență. Al treilea — ingineresc: pentru animațiile UI, 60 FPS asigură o întârziere sub-milisecundă a răspunsului la atingere, ceea ce este critic pentru introducerea textului, derulare și glisare.
Pentru dezvoltatorii mobili, 60 FPS nu este doar o recomandare, ci un buget strict de 16.6 ms per cadru. Acest buget este împărțit între toate fazele de randare: Input (1–2 ms), Animation (2–3 ms), Layout (3–5 ms), Draw (3–5 ms) și Swap (1–2 ms). Dacă vreo fază depășește sub-bugetul său, cadrul poate să nu se încadreze în 16.6 ms. Google Android Performance recomandă să te încadrezi în 12–14 ms pentru pregătirea cadrului, lăsând 2–4 ms rezervă pentru întreruperile de sistem (GC, fire de fundal). Conform Firebase Performance, aplicațiile cu un FPS mediu sub 52 și P99 FPS sub 30 primesc cu 35% mai multe plângeri de performanță în recenziile Google Play.
FPS și Frame Time (timpul de cadru) — sunt două fețe ale aceleiași metrici și este important să nu le confundăm. FPS este viteza, Frame Time este întârzierea. La 60 FPS, fiecare cadru durează 16.6 ms. La 30 FPS — 33.3 ms. Dar FPS este o metrică neliniară: scăderea de la 60 la 30 FPS înseamnă că timpul de cadru s-a dublat, iar scăderea de la 30 la 20 — de 1.5 ori. De aceea, profilatoarele arată nu FPS, ci Frame Time — acest lucru permite vizualizarea cadrelor problematice, nu a frecvenței medii. De exemplu, o medie de 55 FPS poate ascunde că 5% dintre cadre au un Frame Time de 50–100 ms — aceste cadre provoacă Jank, dar nu afectează semnificativ FPS-ul mediu.
La analiza performanței, se recomandă să te uiți nu la FPS-ul mediu, ci la histograma Frame Time. În Android Studio Profiler și iOS Instruments, Frame Time este afișat ca o scară, unde zona verde — până la 16.6 ms (60 FPS), galben — 16.6–33.3 ms (30–60 FPS), roșu — peste 33.3 ms (sub 30 FPS). Fiecare coloană roșie este o întârziere vizibilă pentru utilizator. Regula practică: P95 Frame Time (95% dintre cadre se încadrează în X ms) — este o metrică mai fiabilă decât FPS-ul mediu. Dacă P95 Frame Time depășește 32 ms (30 FPS), aplicația este percepută ca fiind lentă chiar și cu un FPS mediu de 50.
Funcția în Kotlin pentru conversia unui tablou de timpi de cadru în FPS cu percentile. Returnează nu doar FPS-ul mediu, ci și P50, P90 și P99 pentru o analiză detaliată.
data class FpsReport(
val average: Float,
val p50: Float,
val p90: Float,
val p99: Float
)
fun List<Long>.toFpsReport(): FpsReport {
val fpsValues = this.map { ms ->
if (ms > 0) 1000f / ms else 0f
}.sorted()
return FpsReport(
average = fpsValues.average().toFloat(),
p50 = fpsValues[fpsValues.size / 2],
p90 = fpsValues[(fpsValues.size * 90 / 100)],
p99 = fpsValues[(fpsValues.size * 99 / 100)]
)
}
Dispozitivele mobile moderne cu ecrane de 90, 120 și 144 Hz impun noi cerințe pentru FPS. Dacă o aplicație livrează 60 FPS pe un ecran de 120 Hz, utilizatorul vede micro-blocaje, deoarece fiecare al doilea ciclu de reîmprospătare a ecranului primește același cadru. Pentru a menține 120 FPS, bugetul per cadru se reduce de la 16.6 la 8.3 ms — acest lucru necesită un cod de randare de două ori mai eficient. Conform dezvoltatorilor Android (Google I/O 2023), pentru a atinge 120 FPS stabil, este necesar: să eviți alocările în bucla Draw, să minimizezi numărul de View-uri în ierarhie (sub 80), să renunți la drawable-urile grele în favoarea VectorDrawable și să folosești surfaceView pentru grafica complexă.
În iOS, situația este similară: iPhone Pro cu ProMotion (120 Hz) necesită de două ori mai multe cadre, dar timpul pentru fiecare cadru este la jumătate. Apple observă că nu toate animațiile trebuie să ruleze la 120 FPS — Core Animation reduce automat frecvența pentru elementele statice sau care se schimbă lent. Cu toate acestea, derularea, animațiile de gesturi și tranzițiile trebuie să livreze 120 FPS pentru senzația de "mătăsos". Principalele probleme la trecerea de la 60 la 120 FPS: creșterea consumului de energie (cu 25–40% pentru GPU), încălzirea dispozitivului și throttling-ul — când frecvența scade din cauza supraîncălzirii. Se recomandă implementarea unui mecanism de fallback: dacă Frame Time depășește constant 8.3 ms, reduce programatic frecvența țintă la 60 FPS, în loc să aștepți throttling-ul de sistem.
Codul în Java pentru Android determină dacă dispozitivul poate suporta 120 FPS și comută modul de randare. Folosește Display.getMode pentru a determina frecvențele suportate.
class FpsModeSwitcher {
static boolean canDo120Fps(Activity activity) {
Display display = activity.getWindowManager()
.getDefaultDisplay();
for (Display.Mode mode : display.getSupportedModes()) {
if (mode.getRefreshRate() >= 120f) {
return true;
}
}
return false;
}
}
Optimizarea FPS necesită o abordare sistematică, începând cu profilarea și terminând cu refactorizarea locurilor problematice. Prima etapă — măsurarea FPS-ului curent cu un profiler (. A doua etapă — găsirea cadrelor care depășesc bugetul. Pentru Android, acest lucru se poate face prin GPU Profiling sau Perfetto. Pentru iOS — Instruments cu șablonul Core Animation. A treia etapă — eliminarea cauzelor: reducerea overdraw-ului, reducerea adâncimii de înnestire a View-urilor, înlocuirea fazei Layout cu ConstraintLayout, adăugarea ViewHolder Recycling, mutarea calculelor grele într-un fir de fundal.
Optimizările specifice FPS includ: Frame Pacing — un mecanism care distribuie uniform timpul între cadre pentru a evita "pachetele" de cadre rapide și lente. În Android, Choreographer.FrameCallback cu un interval fix permite implementarea Frame Pacing. În iOS, CADisplayLink.preferredFrameRateRange face același lucru. Al doilea mecanism — Triple Buffering: sistemul folosește trei buffere în loc de două, ceea ce permite GPU-ului să înceapă desenarea următorului cadru fără a aștepta eliberarea precedentului. Android activează automat Triple Buffering atunci când este necesar, dar în iOS, dezvoltatorul poate solicita explicit acest lucru prin CAMetalLayer. Al treilea — Texture Caching: stocarea în cache a bitmap-urilor în memoria GPU pentru a nu le reîncărca la fiecare cadru.
Exemplul în Kotlin demonstrează implementarea Frame Pacing cu un interval fix de 16.6 ms. Toate callback-urile sosesc cu un interval uniform, chiar dacă sistemul întârzie.
class PacedFrameRenderer {
private val targetDelta = 16_666_666L // 16.6 ms (60 FPS)
private var lastFrameTime = 0L
private val frameCallback =
Choreographer.FrameCallback { frameTimeNanos ->
val delta = frameTimeNanos - lastFrameTime
if (delta >= targetDelta) {
onFrame(delta)
lastFrameTime = frameTimeNanos
}
Choreographer.getInstance()
.postFrameCallback(this)
}
private fun onFrame(delta: Long) {
// redarea cadrului
}
}
Întrebări frecvente
60 FPS — nivel confortabil pentru aplicațiile mobile. Diferența între 60 și 120 FPS este vizibilă doar pe ecranele cu rată de reîmprospătare înaltă la animații rapide (derulare, glisare). Sub 30 FPS — disconfort.
FPS = 1000 / FrameTime (ms). Dacă Frame Time = 16.6 ms, FPS = 60. Dacă Frame Time = 33.3 ms, FPS = 30. Se recomandă monitorizarea Frame Time, nu a FPS, deoarece acesta arată cadrele problematice.
În timpul derulării, sistemul apelează Layout și Draw pentru fiecare element nou al listei. Dacă View-urile sunt complexe, Layout nu este stocat în cache sau se folosesc drawable-uri grele — Frame Time crește și FPS scade. Soluția — ViewHolder recycling și ierarhie plată.
Folosește Instruments cu șablonul Core Animation (arată FPS în timp real). Pentru măsurarea programatică — CADisplayLink cu numărarea cadrelor pe secundă. Pentru producție — MetricKit cu metrica MXAnimatoryMetric.
Triple Buffering folosește trei buffere în loc de două, permițând GPU-ului să înceapă randarea următorului cadru înainte de finalizarea VSync-ului celui curent. Acest lucru netezește sarcinile de vârf și crește stabilitatea FPS, dar adaugă o întârziere de 1 cadru.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și