FPS в мобилните приложения: същност, изчисление и оптимизация

Автор: IT Sectr Публикувано: 2026-04-01 Време за четене: 11 мин

FPS (Frames Per Second) — е метрика, която показва колко отделни кадъра графичната система рендерира за една секунда. В мобилното разработване FPS е стандартен показател за производителност на UI: колкото по-висок е FPS, толкова по-гладки са анимациите и по-отзивчив е интерфейсът. Според данни на Google Android Performance, 2025, целевата стойност на FPS за мобилни приложения е 60 кадъра в секунда — това е прагът, при който човешкото око възприема движението като непрекъснато и плавно.

Основни точки

  • FPS — брой кадри в секунда, основна метрика за плавност на UI.
  • Целева стойност — 60 FPS, време за кадър — 16.6 мс.
  • За дисплеи с висока честота на опресняване се изисква 120 FPS (8.3 мс на кадър).
  • Падането на FPS под 30 се вижда с просто око като заекване и забавяне.
  • Мониторингът на FPS в продукция помага за откриване на регресии в производителността.

Какво е FPS

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 в мобилно приложение се основава на измерване на времето между последователни кадри. Най-простата формула: 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 — очакваното време на следващия. Разликата между тях е времевият бюджет за текущия кадър.

Мониторинг на FPS чрез CADisplayLink

Кодът в Swift демонстрира прост мониторинг на FPS чрез CADisplayLink. Броячът frameCount се увеличава при всяко извикване и веднъж в секунда се изчислява действителният FPS.

swift
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 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 (време на кадър) — са две страни на една и съща метрика и е важно да не ги бъркаме. FPS е скорост, Frame Time е забавяне. При 60 FPS всеки кадър отнема 16.6 мс. При 30 FPS — 33.3 мс. Но FPS е нелинейна метрика: падането от 60 на 30 FPS означава, че времето на кадър се е удвоило, а падането от 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.

Конвертиране на Frame Time в FPS

Функция в Kotlin за конвертиране на масив от времена на кадри в FPS с персентили. Връща не само средния FPS, но и P50, P90 и P99 за детайлен анализ.

kotlin
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)]
    )
}

Висок FPS и нови дисплеи

Съвременните мобилни устройства с дисплеи 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, вместо да чакате системен тротлинг.

Превключвател между 60 и 120 FPS

Код в Java за Android определя дали устройството може да поддържа 120 FPS и превключва режима на рендериране. Използва Display.getMode за определяне на поддържаните честоти.

java
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 изисква систематичен подход, започвайки с профилиране и завършвайки с рефакториране на проблемните места. Първи етап — измерете текущия 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 паметта, за да не се презареждат при всеки кадър.

Frame Pacing чрез Choreographer

Примерът в Kotlin демонстрира имплементация на Frame Pacing с фиксиран интервал от 16.6 мс. Всички обратни извиквания пристигат с равномерен интервал, дори ако системата закъснява.

kotlin
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) {
        // рендериране на кадър
    }
}

Често задавани въпроси

Какъв FPS се счита за комфортен за потребителя?

60 FPS — комфортно ниво за мобилни приложения. Разликата между 60 и 120 FPS се забелязва само на дисплеи с висока честота на опресняване при бързи анимации (скролване, плъзгане). Под 30 FPS — дискомфорт.

Как FPS е свързан с времето на кадър?

FPS = 1000 / FrameTime (ms). Ако Frame Time = 16.6 мс, FPS = 60. Ако Frame Time = 33.3 мс, FPS = 30. Препоръчва се да следите Frame Time, а не FPS, тъй като той показва проблемните кадри.

Защо FPS пада при скролване?

При скролване системата извиква Layout и Draw за всеки нов елемент от списъка. Ако View-тата са сложни, Layout не се кешира или се използват тежки drawable — Frame Time расте и FPS пада. Решение — ViewHolder recycling и плоска йерархия.

Как да измерим FPS в iOS?

Използвайте Instruments с шаблон Core Animation (показва FPS в реално време). За програмно измерване — CADisplayLink с броене на кадри в секунда. За продукция — MetricKit с метрика MXAnimatoryMetric.

Какво е Triple Buffering и как влияе на FPS?

Triple Buffering използва три буфера вместо два, позволявайки на GPU да започне рендериране на следващия кадър преди завършване на VSync на текущия. Това изглажда пиковите натоварвания и повишава стабилността на FPS, но добавя 1 кадър забавяне.

Обобщение

  • FPS — ключова метрика за плавност на интерфейса, целева стойност — 60 кадъра в секунда.
  • Frame Time (време на кадър) — по-точен показател от FPS, особено персентилите P95 и P99.
  • За 120 Hz дисплеи се изисква 120 FPS с бюджет от 8.3 мс на кадър.
  • Основни причини за спад на FPS — overdraw, дълбоко влагане на View, алокации в Draw цикъла.
  • Frame Pacing и Triple Buffering помагат за изглаждане на неравномерността на времената на кадрите.
  • Профилиране на FPS — чрез GPU Profiling (Android), Instruments Core Animation (iOS), Firebase Performance.
  • Мониторингът на P95 Frame Time в продукция е критичен за откриване на регресии преди масови оплаквания от потребители.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също