Jank для мобільних додатків — що це таке, причини та усунення

Автор: IT Sectr Опубліковано: 2026-04-01 Час читання: 10 хв

Jank — це термін, що позначає помітні запинки або «заїкання» в анімації інтерфейсу, спричинені пропуском окремих кадрів. У мобільних додатках Jank виникає, коли час рендерингу кадру перевищує бюджет, виділений частотою оновлення дисплея. За даними Android Developers, 2025, Jank є основною причиною суб'єктивного відчуття «гальмувань» — додаток може бути функціонально ідеальним, але користувач сприймає його як повільний через нестабільний FPS.

Головне

  • Jank — пропущені кадри, помітні як запинки анімації.
  • Основна причина — перевищення бюджету часу на кадр (16.6 мс для 60 FPS).
  • Jank виникає через важкий Layout, тривалу GC, блокування main thread або overdraw.
  • Для діагностики використовуються FrameTimeline (Android) і Instruments (iOS).
  • Усунення Jank підвищує NPS і утримання користувачів на 15–25%.

Що таке Jank

Jank — це термін з області комп'ютерної графіки, що позначає візуальний дефект, при якому анімація рухається ривками замість плавного ковзання. У мобільній розробці Jank вимірюється як кількість пропущених кадрів (skipped frames) в одиницю часу. Якщо система не встигає підготувати кадр до моменту VSync, дисплей повторює попередній кадр — виникає пауза тривалістю 16.6 мс при 60 Гц. Один пропущений кадр може бути непомітним, але серія з 3–5 пропущених кадрів поспіль створює відчуття «гальма» тривалістю 50–80 мс, яке користувач чітко фіксує.

Jank особливо критичний для анімацій, які повинні працювати з постійною швидкістю: скрол стрічки, анімація відкриття меню, паралакс-ефекти, переходи між екранами. За даними UX-дослідження Google (2024), додаток з Jank-показником більше 3% скрол-сесій отримує на 22% більше однозіркових відгуків, ніж додаток з показником менше 0.5%. Інструмент Android Vitals автоматично відстежує Jank і класифікує його за severity: помірний, серйозний і критичний.

Основні причини Jank

Причини Jank поділяються на кілька категорій. Перша — Layout Jank: викликається частими requestLayout() через зміну розмірів View, анімацій LayoutTransition або динамічного завантаження контенту. Кожен виклик requestLayout запускає Measure + Layout для всього піддерева View, що може займати 5–30 мс. Друга — Draw Jank: пов'язаний з перемальовуванням (overdraw) і використанням важких drawable. Третя — Thread Jank: блокування main thread через синхронні операції — завантаження файлів, робота з БД на головному потоці, декодування Bitmap.

Четверта категорія — GC Jank: збирання сміття (Garbage Collection) в ART/Dalvik або Swift ARC. Коли в купі накопичується багато об'єктів, GC запускає Stop-The-World паузу тривалістю 5–15 мс. На Android GC-паузи найчастіше виникають при частих алокаціях в циклах: створення об'єктів в onDraw(), allocation в адаптерах, невикористовувані lambda-вирази. П'ята — IPC Jank: міжпроцесна взаємодія (ContentProvider, Binder) на головному потоці. Шоста — Rendering Jank: повільний рендеринг GPU через неоптимальні шейдери або текстури великого розміру.

Тип JankПричинаТипова тривалістьІнструмент пошуку
LayoutrequestLayout, relayout5–30 мсPerfetto, Systrace
DrawOverdraw, важкі drawable3–20 мсGPU Profiling
ThreadБлокування main thread10–200 мсAndroid Studio Profiler
GCGarbage Collection5–15 мсMemory Profiler
RenderingЗавантаження GPU10–50 мсGPU Tracer, Xcode GPU

Діагностика Jank в Android

В Android діагностика Jank починається з системного трейсу Perfetto. Perfetto записує активність всіх потоків, CPU, GPU і планувальника. Наочний індикатор Jank — рядки Choreographer.doFrame і Choreographer.doCallbacks: якщо між двома послідовними викликами doFrame інтервал перевищує 16.6 мс, кадр пропущено. Perfetto показує точну причину — який system call, lock або GC викликав затримку. В Android Studio Profiler аналогічний функціонал доступний через CPU Profiler.

Для автоматичного виявлення Jank в продакшні використовується FrameMetricsAggregator — API, який збирає статистику по кожному кадру і агрегує за сесію. В Android 12+ з'явився PerformanceHintManager — API для підказок системі про цільову частоту кадрів. Якщо додаток вказує, що працює в сценарії 120 FPS, система може підвищити частоту CPU/GPU для запобігання Jank. Для простого логування всіх пропущених кадрів достатньо підписатися на Choreographer.FrameCallback.

Логування Jank через Choreographer

Код на Kotlin підписується на Choreographer.FrameCallback і логує кожен пропущений кадр із зазначенням тривалості затримки. Колбек викликається на кожен VSync.

kotlin
class JankDetector {

    private val frameBudget = 16_666_666L
    private var previousFrameTime = 0L

    private val callback =
        Choreographer.FrameCallback { currentTime ->
            if (previousFrameTime != 0L) {
                val frameDuration =
                    currentTime - previousFrameTime
                val skippedFrames =
                    (frameDuration / frameBudget) - 1
                if (skippedFrames > 0) {
                    Log.w("Jank",
                        "Skipped $skippedFrames frames")
                }
            }
            previousFrameTime = currentTime
            Choreographer.getInstance()
                .postFrameCallback(this)
        }

    fun start() {
        Choreographer.getInstance()
            .postFrameCallback(callback)
    }
}

Діагностика Jank в iOS

В iOS діагностика Jank виконується через Instruments з шаблоном Core Animation. Instruments показує FPS в реальному часі, кількість offscreen-рендерів і hit-тести. Основні індикатори Jank в iOS: червоні стовпці в шкалі часу Core Animation (перевищення бюджету кадру), високий показник Renderer (означає offscreen rendering) і низький FPS. Для продакшн-моніторингу MetricKit збирає звіти з метрикою MXAnimatoryMetric, яка включає середній FPS, P50 і P95 час кадру.

Нативна діагностика Jank в iOS включає CADisplayLink з перевіркою timestamp і targetTimestamp. Якщо поточний timestamp суттєво відстає від targetTimestamp, значить було пропущено один або декілька кадрів. Apple також рекомендує використовувати os_signpost для кастомного профілювання: ставити signpost-interval на початку і в кінці відмальовування кадру і дивитися в Instruments, які інтервали перевищують 16.6 мс. В SwiftUI для діагностики Jank використовується UIView.invalidateIntrinsicContentSize — частий виклик цього методу говорить про нестабільний Layout.

CADisplayLink для детекції Jank

Код на Swift визначає пропущені кадри через CADisplayLink. Якщо різниця між timestamp і targetTimestamp перевищує 16.6 мс — фіксується Jank.

swift
class JankMonitor {

    private var displayLink: CADisplayLink?
    private var totalJank = 0

    func start() {
        displayLink = CADisplayLink(
            target: self,
            selector: #selector(detectJank)
        )
        displayLink?.add(to: .current,
            forMode: .common)
    }

    @objc
    private func detectJank() {
        guard let link = displayLink else { return }
        let delay = link.targetTimestamp
            - link.timestamp
        if delay > 0.0167 {
            totalJank += 1
        }
    }
}

Інструменти профілювання Jank

Для профілювання Jank використовуються як вбудовані інструменти ОС, так і сторонні SDK. В Android ключовий інструмент — Perfetto (замінив Systrace). Perfetto дозволяє записувати трейси тривалістю до 30 секунд і аналізувати їх через веб-інтерфейс ui.perfetto.dev. Він показує точну часову шкалу з роботою Choreographer, потоками рендерингу (RenderThread) і GPU. Для детального аналізу проблем з GPU використовується AGI (Android GPU Inspector), який показує не тільки час кадру, але й завантаження конкретних блоків GPU — шейдерів, растеризатора, текстурного блоку.

В iOS аналог — Instruments з шаблонами Core Animation, Metal System Trace і GPU Driver. Core Animation показує FPS і час кадру, Metal System Trace — роботу GPU з деталізацією до кожного draw call. Для профілювання на реальних пристроях під навантаженням використовуються Firebase Performance (збирає Screen Rendering metric) і Sentry (захоплює stack trace при Jank). Новий API Android 15 Performance Hint дозволяє розробнику вказувати системі, які кадри важливі, і отримувати від системи попередження при наближенні Jank.

FrameMetricsAggregator в продакшні

Код на Kotlin використовує FrameMetricsAggregator для збору статистики по кадрах за сесію. Після зупинки агрегатора виводиться кількість пропущених кадрів.

kotlin
class JankAggregator(private val activity: Activity) {

    private val aggregator = FrameMetricsAggregator()

    fun startCollection() {
        aggregator.add(activity.window)
    }

    fun stopAndReport() {
        aggregator.remove()
        val result = aggregator.getMetrics()
        val totalFrames = result
            ?.get(FrameMetrics.TOTAL_DURATION)
            ?.size ?: 0
        val jankFrames = result
            ?.get(FrameMetrics.TOTAL_DURATION)
            ?.count { it > 16_666_666L} ?: 0
        Log.d("JankReport",
            "Jank ratio: \${jankFrames * 100 / totalFrames}%")
    }
}

Методи усунення запинок кадрів

Усунення Jank вимагає комбінації методик залежно від його типу. Для Layout Jank: замінити глибокі ієрархії на ConstraintLayout/Compose/SwiftUI, використовувати merge-теги, уникати requestLayout в анімаціях. Для Draw Jank: використовувати Debug GPU Overdraw для пошуку 4x+ overdraw, замінити важкі drawable на векторні (VectorDrawable/PDF), використовувати hardware layers з обережністю — вони прискорюють відмальовування, але споживають більше GPU-пам'яті. Для Thread Jank: перенести всі I/O операції, роботу з БД і декодування Bitmap в фонові потоки, використовувати Kotlin Coroutines з правильним Dispatcher або RxJava з Schedulers.io().

Для GC Jank: мінімізувати алокації в onDraw() і getView(), використовувати пули об'єктів (ObjectPool), замінити for-each на індексований for, використовувати immutable data class в Kotlin з copy() акуратно — copy створює новий об'єкт. Для IPC Jank: ініціалізувати ContentProvider ліниво через App Startup, перенести Binder-виклики в фоновий потік. Для Rendering Jank: зменшити розмір текстур до максимальної роздільної здатності екрана, використовувати ASTC або ETC2 стиснення, уникати зайвих shader compilation (компілювати шейдери заздалегідь). Комплексне рішення — регулярний запуск Perfetto/Instruments профілювання в CI і відстеження Jank-регресій.

Anti-Jank паттерн: Async Layout

Котлін-код демонструє асинхронне підвантаження даних на екран після reportFullyDrawn, щоб важка робота не блокувала перший кадр. Колбек викликається після того, як користувач бачить інтерфейс.

kotlin
class JankSafeLoader {

    suspend fun loadAfterFirstFrame(
        activity: Activity
    ) {
        // гарантуємо, що перший кадр вже відрендерено
        if (Build.VERSION.SDK_INT >= 29) {
            activity.reportFullyDrawn()
        }

        // важке завантаження — після першого кадру
        withContext(Dispatchers.IO) {
            val data = fetchHeavyData()
            withContext(Dispatchers.Main) {
                updateUI(data)
            }
        }
    }
}

Часті запитання

Що таке Jank в мобільних додатках?

Jank — це пропущені кадри рендерингу, які проявляються у вигляді помітних запинок або ривків анімації. Виникає, коли час підготовки кадру перевищує бюджет часу (16.6 мс для 60 FPS).

Які основні причини Jank?

Layout Jank (часті requestLayout), Draw Jank (overdraw), Thread Jank (блокування main thread), GC Jank (збирання сміття), IPC Jank (Binder-виклики) і Rendering Jank (важкі шейдери).

Як діагностувати Jank в Android?

Використовуйте Perfetto для системного трейсу, GPU Profiling для аналізу фаз кадру і FrameMetricsAggregator для продакшн-моніторингу. В Android Studio — CPU Profiler з Deep Java Trace.

Як виміряти Jank в iOS?

Через Instruments з шаблоном Core Animation або Metal System Trace. Для продакшну — MetricKit з MXAnimatoryMetric. Програмно — CADisplayLink з перевіркою різниці timestamp і targetTimestamp.

Який відсоток Jank вважається критичним?

За даними Google, Jank-показник більше 3% скрол-сесій (3 зі 100 скролів містять запинку) призводить до зростання негативних відгуків на 22%. Цільовий показник — менше 0.5% скрол-сесій.

Підсумки

  • Jank — пропущені кадри, що викликають видимі запинки анімації в мобільних додатках.
  • Основні причини: Layout Jank, Draw Jank, Thread Jank, GC Jank, IPC Jank і Rendering Jank.
  • Діагностика Jank в Android — через Perfetto, GPU Profiling і FrameMetricsAggregator.
  • Діагностика Jank в iOS — через Instruments, CADisplayLink і MetricKit.
  • Усунення Jank вимагає комбінації: плоскі ієрархії, фонові потоки, мінімізація алокацій, кешування.
  • Цільовий Jank-показник — менше 0.5% скрол-сесій із запинками.
  • Регулярне профілювання в CI запобігає регресіям продуктивності до потрапляння в продакшн.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також