Jank dla aplikacji mobilnych — co to jest, przyczyny i eliminacja

Autor: IT Sectr Opublikowano: 2026-04-01 Czas czytania: 10 min

Jank — to termin oznaczający zauważalne zacięcia lub „jąkanie” animacji interfejsu, spowodowane pominięciem poszczególnych klatek. W aplikacjach mobilnych Jank występuje, gdy czas renderowania klatki przekracza budżet przydzielony przez częstotliwość odświeżania wyświetlacza. Według Android Developers, 2025, Jank jest główną przyczyną subiektywnego odczucia „spowolnienia” — aplikacja może być funkcjonalnie idealna, ale użytkownik postrzega ją jako wolną z powodu niestabilnego FPS.

Najważniejsze

  • Jank — pominięte klatki, zauważalne jako zacięcia animacji.
  • Główna przyczyna — przekroczenie budżetu czasu na klatkę (16.6 ms dla 60 FPS).
  • Jank powstaje z powodu ciężkiego Layout, długiego GC, blokowania main thread lub overdraw.
  • Do diagnostyki używa się FrameTimeline (Android) i Instruments (iOS).
  • Eliminacja Jank zwiększa NPS i utrzymanie użytkowników o 15–25%.

Co to jest Jank

Jank — to termin z dziedziny grafiki komputerowej oznaczający wadę wizualną, przy której animacja porusza się szarpnięciami zamiast płynnego przesuwania. W programowaniu mobilnym Jank mierzy się jako liczbę pominiętych klatek (skipped frames) na jednostkę czasu. Jeśli system nie zdąży przygotować klatki na moment VSync, wyświetlacz powtarza poprzednią klatkę — powstaje pauza trwająca 16.6 ms przy 60 Hz. Jedna pominięta klatka może być niezauważalna, ale seria 3–5 pominiętych klatek z rzędu tworzy odczucie „spowolnienia” trwającego 50–80 ms, które użytkownik wyraźnie rejestruje.

Jank jest szczególnie krytyczny dla animacji, które powinny działać ze stałą prędkością: przewijanie listy, animacja otwierania menu, efekty paralaksy, przejścia między ekranami. Według UX-badań Google (2024), aplikacja ze wskaźnikiem Jank powyżej 3% sesji przewijania otrzymuje o 22% więcej jedno gwiazdkowych recenzji niż aplikacja ze wskaźnikiem poniżej 0.5%. Narzędzie Android Vitals automatycznie śledzi Jank i klasyfikuje go według severity: moderate, severe i critical.

Główne przyczyny Jank

Przyczyny Jank dzielą się na kilka kategorii. Pierwsza — Layout Jank: wywoływany przez częste requestLayout() z powodu zmiany rozmiarów View, animacji LayoutTransition lub dynamicznego ładowania treści. Każde wywołanie requestLayout uruchamia Measure + Layout dla całego poddrzewa View, co może zajmować 5–30 ms. Druga — Draw Jank: związany z przerysowywaniem (overdraw) i używaniem ciężkich drawable. Trzecia — Thread Jank: blokowanie main thread z powodu synchronicznych operacji — ładowanie plików, praca z bazą danych na głównym wątku, dekodowanie Bitmap.

Czwarta kategoria — GC Jank: garbage collection w ART/Dalvik lub Swift ARC. Gdy na stercie gromadzi się wiele obiektów, GC uruchamia pauzę Stop-The-World trwającą 5–15 ms. Na Androidzie GC-pauzy najczęściej występują przy częstych alokacjach w pętlach: tworzenie obiektów w onDraw(), alokacje w adapterach, nieużywane wyrażenia lambda. Piąta — IPC Jank: komunikacja międzyprocesowa (ContentProvider, Binder) na głównym wątku. Szósta — Rendering Jank: wolne renderowanie GPU z powodu nieoptymalnych shaderów lub tekstur dużego rozmiaru.

Typ JankPrzyczynaTypowy czas trwaniaNarzędzie wyszukiwania
LayoutrequestLayout, relayout5–30 msPerfetto, Systrace
DrawOverdraw, ciężkie drawable3–20 msGPU Profiling
ThreadBlokowanie main thread10–200 msAndroid Studio Profiler
GCGarbage Collection5–15 msMemory Profiler
RenderingObciążenie GPU10–50 msGPU Tracer, Xcode GPU

Diagnostyka Jank w Android

W Android diagnostyka Jank zaczyna się od systemowego trasu Perfetto. Perfetto rejestruje aktywność wszystkich wątków, CPU, GPU i planisty. Wyraźnym wskaźnikiem Jank są linie Choreographer.doFrame i Choreographer.doCallbacks: jeśli między dwoma kolejnymi wywołaniami doFrame odstęp przekracza 16.6 ms, klatka jest pominięta. Perfetto pokazuje dokładną przyczynę — który system call, lock lub GC spowodował opóźnienie. W Android Studio Profiler podobna funkcjonalność jest dostępna przez CPU Profiler.

Do automatycznego wykrywania Jank w produkcji używa się FrameMetricsAggregator — API, które zbiera statystyki dla każdej klatki i agreguje na sesję. W Android 12+ pojawił się PerformanceHintManager — API do podpowiadania systemowi docelowej częstotliwości klatek. Jeśli aplikacja wskazuje, że pracuje w scenariuszu 120 FPS, system może podnieść częstotliwość CPU/GPU, aby zapobiec Jank. Do prostego logowania wszystkich pominiętych klatek wystarczy zasubskrybować Choreographer.FrameCallback.

Logowanie Jank przez Choreographer

Kod w Kotlin subskrybuje Choreographer.FrameCallback i loguje każdą pominiętą klatkę z podaniem czasu opóźnienia. Callback jest wywoływany przy każdym 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",
                        "Pominięto $skippedFrames klatek")
                }
            }
            previousFrameTime = currentTime
            Choreographer.getInstance()
                .postFrameCallback(this)
        }

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

Diagnostyka Jank w iOS

W iOS diagnostyka Jank jest wykonywana przez Instruments z szablonem Core Animation. Instruments pokazuje FPS w czasie rzeczywistym, liczbę offscreen-renderów i hit-testy. Główne wskaźniki Jank w iOS: czerwone kolumny na skali czasu Core Animation (przekroczenie budżetu klatki), wysoki wskaźnik Renderer (oznacza offscreen rendering) i niski FPS. Do monitorowania produkcji MetricKit zbiera raporty z metryką MXAnimatoryMetric, która obejmuje średni FPS, P50 i P95 czasu klatki.

Natywna diagnostyka Jank w iOS obejmuje CADisplayLink ze sprawdzaniem timestamp i targetTimestamp. Jeśli bieżący timestamp znacznie odbiega od targetTimestamp, oznacza to, że pominięto jedną lub więcej klatek. Apple zaleca również używanie os_signpost do niestandardowego profilowania: umieszczanie signpost-interval na początku i końcu renderowania klatki i sprawdzanie w Instruments, które interwały przekraczają 16.6 ms. W SwiftUI do diagnostyki Jank używa się UIView.invalidateIntrinsicContentSize — częste wywoływanie tej metody świadczy o niestabilnym Layout.

CADisplayLink do detekcji Jank

Kod w Swift określa pominięte klatki przez CADisplayLink. Jeśli różnica między timestamp a targetTimestamp przekracza 16.6 ms — rejestrowany jest 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
        }
    }
}

Narzędzia do profilowania Jank

Do profilowania Jank używa się zarówno wbudowanych narzędzi systemu operacyjnego, jak i zewnętrznych SDK. W Android kluczowym narzędziem jest Perfetto (zastąpił Systrace). Perfetto pozwala nagrywać trasy trwające do 30 sekund i analizować je przez interfejs webowy ui.perfetto.dev. Pokazuje dokładną oś czasu z pracą Choreographer, wątkami renderowania (RenderThread) i GPU. Do szczegółowej analizy problemów z GPU używa się AGI (Android GPU Inspector), który pokazuje nie tylko czas klatki, ale także obciążenie konkretnych bloków GPU — shaderów, rasteryzatora, bloku tekstur.

W iOS odpowiednikiem jest Instruments z szablonami Core Animation, Metal System Trace i GPU Driver. Core Animation pokazuje FPS i czas klatki, Metal System Trace — pracę GPU ze szczegółowością do każdego draw call. Do profilowania na rzeczywistych urządzeniach pod obciążeniem używa się Firebase Performance (zbiera metrykę Screen Rendering) i Sentry (przechwytuje stack trace przy Jank). Nowe API Android 15 Performance Hint pozwala programiście wskazywać systemowi, które klatki są ważne, i otrzymywać od systemu ostrzeżenia przy zbliżaniu się do Jank.

FrameMetricsAggregator w produkcji

Kod w Kotlin używa FrameMetricsAggregator do zbierania statystyk klatek na sesję. Po zatrzymaniu agregatora wyświetlana jest liczba pominiętych klatek.

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",
            "Wskaźnik Jank: ${jankFrames * 100 / totalFrames}%")
    }
}

Metody eliminacji zacięć klatek

Eliminacja Jank wymaga kombinacji metod w zależności od jego typu. Dla Layout Jank: zastąpienie głębokich hierarchii na ConstraintLayout/Compose/SwiftUI, używanie merge-tagów, unikanie requestLayout w animacjach. Dla Draw Jank: używanie Debug GPU Overdraw do znajdowania 4x+ overdraw, zastąpienie ciężkich drawable wektorowymi (VectorDrawable/PDF), ostrożne używanie hardware layers — przyspieszają renderowanie, ale zużywają więcej pamięci GPU. Dla Thread Jank: przeniesienie wszystkich operacji I/O, pracy z bazą danych i dekodowania Bitmap do wątków tła, używanie Kotlin Coroutines z odpowiednim Dispatcher lub RxJava z Schedulers.io().

Dla GC Jank: minimalizowanie alokacji w onDraw() i getView(), używanie pul obiektów (ObjectPool), zastąpienie for-each na indeksowany for, ostrożne używanie immutable data class w Kotlin z copy() — copy tworzy nowy obiekt. Dla IPC Jank: inicjalizowanie ContentProvider leniwie przez App Startup, przeniesienie wywołań Binder do wątku tła. Dla Rendering Jank: zmniejszenie rozmiaru tekstur do maksymalnej rozdzielczości ekranu, używanie kompresji ASTC lub ETC2, unikanie zbędnych shader compilation (kompilowanie shaderów z wyprzedzeniem). Kompleksowym rozwiązaniem jest regularne uruchamianie profilowania Perfetto/Instruments w CI i śledzenie regresji Jank.

Anti-Jank pattern: Async Layout

Kod w Kotlin demonstruje asynchroniczne ładowanie danych na ekran po reportFullyDrawn, aby ciężka praca nie blokowała pierwszej klatki. Callback jest wywoływany po tym, jak użytkownik widzi interfejs.

kotlin
class JankSafeLoader {

    suspend fun loadAfterFirstFrame(
        activity: Activity
    ) {
        // gwarantujemy, że pierwsza klatka jest już narysowana
        if (Build.VERSION.SDK_INT >= 29) {
            activity.reportFullyDrawn()
        }

        // ciężkie ładowanie — po pierwszej klatce
        withContext(Dispatchers.IO) {
            val data = fetchHeavyData()
            withContext(Dispatchers.Main) {
                updateUI(data)
            }
        }
    }
}

Często zadawane pytania

Co to jest Jank w aplikacjach mobilnych?

Jank — to pominięte klatki renderowania, które przejawiają się jako zauważalne zacięcia lub szarpnięcia animacji. Powstaje, gdy czas przygotowania klatki przekracza budżet czasu (16.6 ms dla 60 FPS).

Jakie są główne przyczyny Jank?

Layout Jank (częste requestLayout), Draw Jank (overdraw), Thread Jank (blokowanie main thread), GC Jank (garbage collection), IPC Jank (wywołania Binder) i Rendering Jank (ciężkie shadery).

Jak diagnozować Jank w Android?

Użyj Perfetto do systemowego trasu, GPU Profiling do analizy faz klatki i FrameMetricsAggregator do monitorowania produkcji. W Android Studio — CPU Profiler z Deep Java Trace.

Jak zmierzyć Jank w iOS?

Przez Instruments z szablonem Core Animation lub Metal System Trace. Do produkcji — MetricKit z MXAnimatoryMetric. Programowo — CADisplayLink ze sprawdzaniem różnicy timestamp i targetTimestamp.

Jaki procent Jank jest uznawany za krytyczny?

Według Google, wskaźnik Jank powyżej 3% sesji przewijania (3 na 100 przewinięć zawiera zacięcie) prowadzi do wzrostu negatywnych recenzji o 22%. Docelowy wskaźnik — poniżej 0.5% sesji przewijania.

Podsumowanie

  • Jank — pominięte klatki powodujące widoczne zacięcia animacji w aplikacjach mobilnych.
  • Główne przyczyny: Layout Jank, Draw Jank, Thread Jank, GC Jank, IPC Jank i Rendering Jank.
  • Diagnostyka Jank w Android — przez Perfetto, GPU Profiling i FrameMetricsAggregator.
  • Diagnostyka Jank w iOS — przez Instruments, CADisplayLink i MetricKit.
  • Eliminacja Jank wymaga kombinacji: płaskie hierarchie, wątki tła, minimalizacja alokacji, buforowanie.
  • Docelowy wskaźnik Jank — poniżej 0.5% sesji przewijania z zacięciami.
  • Regularne profilowanie w CI zapobiega regresjom wydajności przed trafieniem do produkcji.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również