FPS (Frames Per Second) — је метрика која показује колико појединачних кадрова графички систем рендерује у једној секунди. У мобилном развоју, FPS је стандардни показатељ перформанси UI-ја: што је FPS већи, то су анимације глађе и интерфејс одзивнији. Према подацима Google Android Performance, 2025, циљна вредност FPS за мобилне апликације износи 60 кадрова у секунди — то је праг на којем људско око перципира покрет као непрекидан и гладак.
Главно
FPS (Frames Per Second) — је јединица мере фреквенције кадрова која се користи у рачунарској графици, видеу и мобилним интерфејсима. Сваки кадар је статична слика која се приказује на екрану током кратког временског периода. При брзој смени кадрова, мозак их перципира као непрекидан покрет — овај ефекат се назива перзистенција вида. За мобилне апликације, FPS је критична метрика, јер сваки пропуштени кадар (drop) претвара глатку анимацију у приметно запињање. Апликација мора да успе да рендерује сваки кадар строго у оквиру временског буџета: 16.6 мс за 60 FPS, 11.1 мс за 90 FPS, 8.3 мс за 120 FPS.
FPS се мери не само за UI, већ и за игре, видео и камеру. У играма, FPS зависи од сложености сцене, квалитета текстура и снаге GPU-ја. У видеу, FPS је фиксан (24, 30, 60 кадрова/с) и одређен је садржајем. У мобилним апликацијама, FPS зависи од ефикасности UI кода: сложености Layout-а, броја View-ова, учесталости прецртавања и рада GC (Garbage Collection). Према Apple WWDC 2022, просечан FPS у апликацији може да падне за 10–15% због неефикасног ажурирања колекција (reloadData уместо insert/delete/dequeueReusableCell). Мерење FPS-а у реалном времену је стандардна пракса за QA инжењере и програмере који раде на перформансама.
Израчунавање FPS у мобилној апликацији заснива се на мерењу времена између узастопних кадрова. Најједноставнија формула: FPS = 1000 / deltaTimeMs, где је deltaTimeMs интервал између завршетка претходног кадра и завршетка тренутног. Ако је тренутни кадар рендерован за 20 мс, FPS = 1000 / 20 = 50. Међутим, у пракси FPS ретко када буде стабилан чак ни у једној секунди: типичан профил укључује кадрове од 12–16 мс, испреплетане са пропуштеним (jank) или спорим кадровима (40–60 мс). Због тога се FPS мери као покретни просек за 1–5 секунди или као перцентили дистрибуције времена кадрова.
У Android-у, FPS се израчунава путем Choreographer, који добија колбек од VSync-а (импулса синхронизације дисплеја). Сваки колбек одговара једном кадру. Ако колбек није стигао — кадар је пропуштен. Choreographer омогућава мерење тачног броја кадрова у секунди и броја пропуштених (skipped frames). У iOS-у, CADisplayLink ради аналогно — позива се сваки пут када је дисплеј спреман за рендеровање новог кадра. Својство timestamp садржи тачно време последњег кадра, а targetTimestamp — очекивано време следећег. Разлика између њих је буџет времена за тренутни кадар.
Код у Swift-у демонстрира једноставно праћење FPS-а путем CADisplayLink. Бројач frameCount се повећава при сваком позиву и једном у секунди се израчунава стварни FPS.
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
}
}
}
Стандард 60 FPS (60 Hz) учврстио се у индустрији из неколико разлога. Први — физиолошки: људско око не разликује појединачне кадрове при фреквенцији изнад 50–60 Hz, перципирајући их као гладак покрет. Овај праг се назива Critical Flicker Fusion (CFF). Други — историјски: прве катодне цеви (CRT) радиле су на 60 Hz у САД (NTSC) и 50 Hz у Европи (PAL). Модерни LCD дисплеји наследили су ову фреквенцију. Трећи — инжењерски: за UI анимације, 60 FPS обезбеђује суб-милисекундно кашњење одговора на додир, што је критично за унос текста, скроловање и превлачење.
За мобилне програмере, 60 FPS није само препорука, већ строги буџет од 16.6 мс по кадру. Овај буџет дели се између свих фаза рендеровања: Input (1–2 мс), Animation (2–3 мс), Layout (3–5 мс), Draw (3–5 мс) и Swap (1–2 мс). Ако нека фаза премаши свој под-буџет, кадар можда неће стати у 16.6 мс. Google Android Performance препоручује да се уложи 12–14 мс на припрему кадра, остављајући 2–4 мс резерве за системске прекиде (GC, позадинске нити). Према Firebase Performance, апликације са просечним FPS-ом испод 52 и P99 FPS испод 30 добијају 35% више жалби на перформансе у Google Play рецензијама.
FPS и Frame Time (време кадра) — две су стране исте метрике и важно их је не мешати. FPS је брзина, Frame Time је кашњење. При 60 FPS, сваки кадар траје 16.6 мс. При 30 FPS — 33.3 мс. Али FPS је нелинеарна метрика: пад са 60 на 30 FPS значи да је време кадра порасло 2 пута, а пад са 30 на 20 — 1.5 пута. Због тога профилисачи приказују не FPS, већ Frame Time — то омогућава увид у проблематичне кадрове, а не у просечну фреквенцију. На пример, просек 55 FPS може да прикрије да 5% кадрова има Frame Time 50–100 мс — ови кадрови изазивају Jank, али не утичу много на просечан FPS.
При анализи перформанси препоручује се гледати не просечан FPS, већ хистограм Frame Time-а. У Android Studio Profiler и iOS Instruments, Frame Time је приказан као скала, где је зелена зона — до 16.6 мс (60 FPS), жута — 16.6–33.3 мс (30–60 FPS), црвена — преко 33.3 мс (испод 30 FPS). Свака црвена колона је приметно кашњење за корисника. Практично правило: P95 Frame Time (95% кадрова стаје у X мс) — поузданија је метрика од просечног FPS-а. Ако P95 Frame Time премашује 32 мс (30 FPS), апликација се доживљава као спора чак и при просечном FPS = 50.
Функција у Kotlin-у за конверзију низа времена кадрова у FPS са перцентилима. Враћа не само просечан FPS, већ и P50, P90 и P99 за детаљну анализу.
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)]
)
}
Модерни мобилни уређаји са дисплејима од 90, 120 и 144 Hz постављају нове захтеве за FPS. Ако апликација даје 60 FPS на 120-херцном дисплеју, корисник види микро-запињање, јер сваки други циклус освежавања екрана добија исти кадар. За одржавање 120 FPS, буџет по кадру се смањује са 16.6 на 8.3 мс — ово захтева двоструко ефикаснији код рендеровања. Према програмерима Android-а (Google I/O 2023), за постизање стабилних 120 FPS потребно је: избегавати алокације у Draw петљи, минимизирати број View-ова у хијерархији (мање од 80), одустати од тешких drawable у корист VectorDrawable-а и користити surfaceView за сложену графику.
У iOS-у ситуација је аналогна: iPhone Pro са ProMotion (120 Hz) захтева двоструко више кадрова, али време за сваки кадар је упола краће. Apple напомиње да не морају све анимације да раде на 120 FPS — Core Animation аутоматски смањује фреквенцију за непокретне или споро променљиве елементе. Међутим, скроловање, анимације гестова и транзиције морају да дају 120 FPS за осећај "свиленкастости". Главни проблеми при преласку са 60 на 120 FPS: повећање потрошње енергије (за 25–40% за GPU), загревање уређаја и тротлинг — када фреквенција опада због прегревања. Препоручује се имплементација fallback механизма: ако Frame Time стабилно премашује 8.3 мс, програмски смањити циљну фреквенцију на 60 FPS, уместо чекања системског тротлинга.
Код у Java за Android одређује да ли уређај може да подржи 120 FPS и пребацује режим рендеровања. Користи Display.getMode за одређивање подржаних фреквенција.
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;
}
}
Оптимизација FPS захтева систематски приступ, почев од профилисања и завршавајући рефакторисањем проблематичних места. Прва фаза — измерити тренутни FPS помоћу профилисача (. Друга фаза — пронаћи кадрове који премашују буџет. За Android, ово се може урадити путем GPU Profiling или Perfetto. За iOS — Instruments са шаблоном Core Animation. Трећа фаза — отклонити узроке: смањити overdraw, смањити дубину угнежђења View-ова, заменити Layout фазу са ConstraintLayout, додати ViewHolder Recycling, преместити тешка израчунавања у позадинску нит.
Специфичне за FPS оптимизације укључују: Frame Pacing — механизам који равномерно распоређује време између кадрова како би се избегли "пакети" брзих и спорих кадрова. У Android-у, Choreographer.FrameCallback са фиксним интервалом омогућава имплементацију Frame Pacing-а. У iOS-у, CADisplayLink.preferredFrameRateRange чини исто. Други механизам — Triple Buffering: систем користи три бафера уместо два, што омогућава GPU-у да почне цртање следећег кадра без чекања на ослобађање претходног. Android аутоматски укључује Triple Buffering када је потребно, али у iOS-у програмер може експлицитно да га затражи путем CAMetalLayer. Трећи — Texture Caching: кеширање битмапа у GPU меморији како се не би поново учитавале при сваком кадру.
Пример у Kotlin-у демонстрира имплементацију Frame Pacing са фиксним интервалом од 16.6 мс. Сви колбекови стижу са равномерним интервалом, чак и ако систем касни.
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) {
// рендеровање кадра
}
}
Често постављана питања
60 FPS — удобан ниво за мобилне апликације. Разлика између 60 и 120 FPS приметна је само на дисплејима са високом фреквенцијом освежавања код брзих анимација (скроловање, превлачење). Испод 30 FPS — неугодност.
FPS = 1000 / FrameTime (ms). Ако је Frame Time = 16.6 мс, FPS = 60. Ако је Frame Time = 33.3 мс, FPS = 30. Препоручује се праћење Frame Time-а, а не FPS-а, јер он показује проблематичне кадрове.
При скроловању, систем позива Layout и Draw за сваки нови елемент листе. Ако су View-ови сложени, Layout се не кешира или користе тешки drawable — Frame Time расте и FPS опада. Решење — ViewHolder recycling и равна хијерархија.
Користите Instruments са шаблоном Core Animation (приказује FPS у реалном времену). За програмско мерење — CADisplayLink са бројањем кадрова у секунди. За продукцију — MetricKit са метриком MXAnimatoryMetric.
Triple Buffering користи три бафера уместо два, омогућавајући GPU-у да започне рендеровање следећег кадра пре завршетка VSync-а тренутног. Ово изглађује вршна оптерећења и повећава стабилност FPS-а, али додаје 1 кадар кашњења.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође