Frame Rate — је број кадрова које графички систем приказује у једној секунди. У мобилним апликацијама фрејм рејт директно одређује глаткоћу анимација, скроловања и прелаза између екрана. Према подацима Android Developers, 2025, циљни Frame Rate износи 60 fps за стандардне дисплеје и 120 fps за уређаје са високом фреквенцијом освежавања. Одступање од циљне вредности доводи до визуелних застоја и погоршања корисничког искуства.
Главно
Frame Rate (фрејм рејт) — је метрика мерена у кадровима у секунди (fps) која показује колико пута у секунди апликација ажурира слику на екрану. Људско око перципира покрет као гладак од 24 fps (кино), али за интерактивни UI потребно је минимално 60 fps како би се додири и анимације осећали тренутно. Сваки кадар је комплетан циклус: обрада корисничког уноса, израчунавање Layout, рендеринг View хијерархије и приказ на екрану. Ако било која од фаза премаши додељени буџет времена (16.6 ms при 60 fps), кадар се пропушта и корисник види застој.
Важно је разликовати Frame Rate апликације од фреквенције освежавања дисплеја (Refresh Rate). Фреквенција освежавања је карактеристика екрана: колико пута у секунди дисплеј физички освежава слику (60, 90, 120 или 144 Hz). Frame Rate — колико кадрова у секунди апликација успева да рендерује. Ако апликација производи 60 fps при дисплеју од 120 Hz, сваки други кадар ће бити дуплиран — слика ће остати глатка, али не толико реагивна колико би могла бити. Према Google I/O 2023, модерни флагмани могу да одржавају 120 fps у једноставним UI сценаријима, али у тешким (игре, сложене листе) фреквенција пада на 40–60 fps.
Рендеринг кадра у мобилној апликацији пролази кроз цевовод од неколико фаза. У Android-у цевовод укључује: обраду уноса (Input), анимацију (Animation), мерење и распоред (Layout), цртање (Draw), синхронизацију са GPU и приказ на екрану (Swap). Свака фаза се извршава на CPU или GPU, а укупно време свих фаза не сме да премаши буџет кадра. За 60 fps буџет је 16.6 ms, за 120 fps — 8.3 ms. Choreographer (Android) и CADisplayLink (iOS) синхронизују рендеринг са вертикалним освежавањем дисплеја (VSync), гарантујући да се кадар приказује само у тренутку освежавања екрана, избегавајући цепање слике (tearing).
У iOS-у цевовод је сличан: Run Loop обрађује догађаје, Core Animation израчунава слојеве, Render Server (одвојени процес) рендерује и шаље кадар на GPU. Разлика iOS-а — одвојени процес Render Server који изолује рендеринг од главне апликације. Ако апликација блокира main thread, Render Server и даље може да прикаже последњи познати кадар, али анимације ће се зауставити. Ако Render Server сам не стигне — GPU мирује и Frame Rate опада. Према Apple WWDC 2022, најчешћи узроци ниског Frame Rate у iOS-у су прекомерно угњежђавање CALayer, тешки shadowPath и рендеринг ван екрана (offscreen rendering).
Код у Kotlin-у се претплаћује на Choreographer.FrameCallback и бележи стварно време између кадрова. Ако интервал прелази 16.6 ms — бележи се пропуштени кадар.
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 (фреквенција освежавања) — хардверска карактеристика дисплеја која одређује колико пута у секунди екран физички прецртава слику. Стандардни дисплеји имају 60 Hz, модерни флагмани — 90, 120 или 144 Hz. Frame Rate апликације може бити нижи, једнак или виши од фреквенције освежавања (у последњем случају вишак кадрова се одбацује). Идеалан сценарио — Frame Rate се поклапа са Refresh Rate: сваки хардверски циклус добија нови кадар од апликације и покрет је максимално гладак. Ако је Frame Rate нижи, дисплеј понавља последњи кадар, што се перципира као микро-застоји (stutter).
Android и iOS подржавају динамичко пребацивање фреквенције освежавања. Android 12+ користи Smart Refresh Rate: при скроловању систем подиже фреквенцију на 120 Hz, при статичном садржају смањује на 60 Hz ради уштеде батерије. iOS ProMotion (iPhone 13 Pro и новији) ради слично — фреквенција варира од 10 до 120 Hz у зависности од садржаја. Програмер треба да провери да ли уређај подржава високу фреквенцију и да прилагоди буџет времена по кадру. Ако апликација не стигне да рендерује кадар за 8.3 ms (за 120 Hz), боље је принудно радити на 60 Hz — то ће обезбедити стабилан Frame Rate без пропуштених кадрова.
| Тип дисплеја | Refresh Rate | Буџет по кадру | Уређаји |
|---|---|---|---|
| Стандардни | 60 Hz | 16.6 ms | Већина Android/iOS |
| Високи | 90 Hz | 11.1 ms | OnePlus, Pixel 6+ |
| Флагмански | 120 Hz | 8.3 ms | iPhone Pro, Galaxy S22+ |
| Гејминг | 144 Hz | 6.9 ms | ROG Phone, Nubia RedMagic |
За мерење Frame Rate у мобилним апликацијама доступни су како уграђени алати платформи, тако и екстерни профилери. У Android-у главни алат је GPU Profiling (Developer Options → Profile GPU Rendering) који приказује временску скалу сваког кадра са раздвајањем по фазама (Draw, Prepare, Process, Execute). Детаљнију анализу пружа Android Studio Profiler — бележи комплетан профил рендеринга са навођењем конкретних View које изазивају прецртавање. У iOS-у се користи Instruments са шаблоном Core Animation — приказује FPS, време рендеринга слојева и број рендеринга ван екрана.
За праћење Frame Rate у продукцији користи се Firebase Performance (Android) — прикупља Frame Rate у позадини и агрегира по уређајима, верзијама ОС и сесијама. У iOS-у MetricKit пружа сличне податке путем MXAnimatoryMetric. За игре и Flutter апликације користе се FrameTimingCallback (Flutter) и Unity Profiler. Важно је мерити не просечан Frame Rate, већ перцентиле: P50, P90 и P99. Апликација може да показује просечних 55 fps, али да има P99 = 30 fps — то значи да 1% времена корисници виде јаке застоје и то је довољно за негативне рецензије.
Пример у Dart-у показује како се претплатити на FrameTimingCallback у Flutter-у и бележити број пропуштених кадрова. Callback се покреће након сваког завршеног кадра.
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}");
}
}
Оптимизација Frame Rate почиње идентификацијом уских грла у цевоводу рендеринга. У фази Layout главни проблеми су прекомерно угњежђавање View хијерархије, коришћење релативних Layout-а (RelativeLayout са великим бројем правила) и чести позиви requestLayout. Решење — коришћење ConstraintLayout или равне хијерархије, избегавање угњежђавања више од 5–6 нивоа. У фази Draw — прецртавање (overdraw): када се пиксел црта неколико пута по кадру. На пример, бела позадина Activity испод полупрозирног фрагмента испод којег је још један слој — сваки пиксел се црта три пута. Алат Debug GPU Overdraw приказује проблематичне зоне бојом. Препоручује се одржавање overdraw на нивоу 2x или нижем.
У iOS-у главни проблеми су тешки cornerRadius и masksToBounds — изазивају рендеринг ван екрана (offscreen rendering), при којем Core Animation ствара привремени бафер, црта у њега, затим копира резултат на екран. Offscreen rendering се лако примећује у Instruments Core Animation: ако је линија Renderer црвена — постоје проблеми. Решење — коришћење UIImageView са унапред исеченим сликама уместо cornerRadius, избегавање groupOpacity и shouldRasterize без крајње нужде. За обе платформе кључно је минимизирати број позива invalidate() и setNeedsDisplay() — сваки такав позив покреће комплетан циклус прецртавања приказа.
Код демонстрира замену дубоког угњежђавања RelativeLayout равном структуром ConstraintLayout. Смањење нивоа угњежђавања са 4 на 1 скраћује време Layout за 30–50%.
// Пример: равна структура кроз 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
// повезивање података без прецртавања целог контејнера
}
}
Модерне мобилне апликације све чешће користе адаптивни Frame Rate — систем који динамички прилагођава циљну фреквенцију тренутном сценарију. При брзом скроловању листа захтева 120 fps за глаткоћу, при статичном екрану довољно је 60 fps или чак 30 fps за видео. У Android-у адаптација се реализује кроз Choreographer.setFrameInterval (API 33+) и Window.setFrameRate. Програмер може да назначи систему жељену фреквенцију: setPreferredRefreshRate у SurfaceView или setFrameRate у Window. iOS аутоматски управља фреквенцијом кроз ProMotion, али програмер може експлицитно да подеси preferredFramesPerSecond за CADisplayLink.
Динамички Frame Rate је посебно важан за игре и апликације са анимацијама. Према Google подацима, смањење Frame Rate са 120 на 60 Hz на статичном екрану штеди до 30–40% енергије GPU. За постизање најбоље равнотеже између глаткоће и потрошње енергије препоручује се: мерити стварни Frame Rate у различитим сценаријима, подесити циљни fps у зависности од сцене (игра — 60, мени — 30, видео — 24) и пребацивати режиме кроз Lifecycle-aware компоненте, како апликација при свођењу не би трошила ресурсе на рендеринг 120 fps у позадини.
Код у Swift-у подешава preferredFramesPerSecond за CADisplayLink у iOS-у. При скроловању фреквенција расте на 120 Hz, при заустављању — опада на 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() {
// ажурирање анимације
}
}
Често постављана питања
За мобилне апликације циљни Frame Rate — 60 fps (16.6 ms по кадру). За уређаје са дисплејима од 120 Hz пожељно је 120 fps. Вредности испод 30 fps приметно погоршавају корисничко искуство.
Frame Rate — колико кадрова у секунди апликација рендерује. Refresh Rate — колико пута у секунди дисплеј физички освежава слику. Када је Frame Rate нижи од Refresh Rate, дисплеј понавља последњи кадар.
Користите GPU Profiling у Developer Options, Android Studio Profiler или Firebase Performance. За програмско мерење — Choreographer.FrameCallback са израчунавањем интервала између кадрова.
Overdraw — прецртавање истог пиксела неколико пута по кадру. Сваки додатни слој повећава време Draw фазе и смањује Frame Rate. Оптималан overdraw је 2x, критичан — 4x и више.
При статичном садржају Dynamic Frame Rate смањује фреквенцију на 30–60 Hz, смањујући оптерећење GPU за 30–40%. При скроловању фреквенција расте на 90–120 Hz ради глаткоће.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође