Lagi w programowaniu mobilnym: co to jest, przyczyny i metody eliminacji

Autor: IT Sectr Opublikowano: 2026-07-28 Czas czytania: 9 min

Lag w aplikacji mobilnej to zauważalne opóźnienie między działaniem użytkownika a reakcją interfejsu, które powstaje z powodu przeciążenia głównego wątku, wycieków pamięci lub nieoptymalnych operacji wejścia-wyjścia. W przeciwieństwie do błędów związanych z logicznymi pomyłkami, lag to problem wydajności: aplikacja działa poprawnie, ale wolno. Według AppDynamics Mobile App Performance Report 2024, 62% użytkowników usuwa aplikację, jeśli działa wolno dłużej niż 3 sekundy. Diagnostyka lagów wymaga profilowania CPU, pamięci i sieci za pomocą Android Studio Profiler i Xcode Instruments.

Najważniejsze

  • Lag — zauważalne opóźnienie interfejsu przy poprawnej pracy aplikacji, spowodowane problemami wydajności
  • Główne przyczyny — blokowanie głównego wątku, wycieki pamięci, częste pauzy GC, nieoptymalne zapytania SQL i wywołania sieciowe
  • Diagnostyka wykonywana przez CPU Profiler, Memory Profiler i Network Profiler w Android Studio oraz Time Profiler w Xcode
  • Eliminacja obejmuje przenoszenie zadań do wątków tła, wprowadzanie buforowania, optymalizację adapterów i leniwe ładowanie danych
  • Profilaktyka — StrictMode, Main Thread Checker, asynchroniczne kolejki GCD i Kotlin Coroutines z odpowiednimi dyspozytorami

Co to jest lag w programowaniu mobilnym

Lag (z ang. lag) w aplikacji mobilnej to subiektywnie odczuwalne opóźnienie między działaniem użytkownika (dotknięcie, przesunięcie, wprowadzenie tekstu) a reakcją interfejsu. Technicznie lag mierzy się jako czas między zdarzeniem wejścia a pełnym renderowaniem klatki: komfortowy próg — do 100 ms, zauważalny — od 200 ms, krytyczny — powyżej 500 ms.

Różnica między lagiem, błędem a spowolnieniem

W terminologii użytkowników „laguje” i „zwalnia” są często używane zamiennie, ale technicznie lag to stałe opóźnienie (np. 300 ms przy każdym kliknięciu), a „zwalnia” — niestałe spowolnienie: aplikacja działa płynnie, po czym zawiesza się na sekundę. Błąd, w przeciwieństwie do laga, dotyczy nie szybkości, a poprawności wyświetlania.

Wpływ lagów na metryki aplikacji

Google Play i App Store uwzględniają wskaźniki wydajności przy rankingu aplikacji. Wskaźnik ANR, częstotliwość jank i czas uruchamiania wpływają na widoczność w wyszukiwarce i konwersję instalacji. Aplikacja z ciągłymi lagami traci do 40% użytkowników po pierwszym uruchomieniu.

Przyczyny lagów i spowolnień w aplikacjach

Lagi powstają, gdy główny wątek UI nie nadąża z przetwarzaniem klatek z częstotliwością 60 FPS (16.6 ms na klatkę) lub 120 FPS (8.3 ms). Rozważmy główne źródła opóźnień.

Blokowanie głównego wątku

Każda synchroniczna operacja w wątku UI — odczyt z SharedPreferences, praca z bazą danych przez Room bez suspend, dekodowanie obrazu do Bitmap — blokuje renderowanie klatki. Na Androidzie prowadzi to do pomijania klatek (jank), na iOS — do opóźnienia renderowania Core Animation.

Wycieki pamięci i częste pauzy GC

Gdy garbage collector (Garbage Collector) w Androidzie lub ARC w iOS wykonuje zwalnianie pamięci, wszystkie wątki są wstrzymywane. Częste pauzy GC powstają przy tworzeniu dużej liczby tymczasowych obiektów — na przykład przy każdym wywołaniu adaptera listy tworzona jest nowa instancja ViewHolder. Przejawia się to jako szarpane przewijanie.

Ciężkie hierarchie layout

Zagnieżdżone ConstraintLayout, wielokrotne LinearLayout, nakładające się View — każde zagnieżdżenie zwiększa czas measure i layout pass. Xcode wskazuje, że głęboka hierarchia warstw (ponad 10 poziomów) powoduje spadek FPS o 20-30%.

  • Android — nadmiarowe requestLayout, nieefektywne łańcuchy ConstraintLayout, duży Bitmap bez downscale
  • iOS — Auto Layout constraints z konfliktami, ciężkie CALayer, shadowPath bez rasteryzacji
  • Cross-platform — synchroniczne wywołania HTTP w wątku UI, ciężkie parsowanie JSON, nieoptymalne obrazy wysokiej rozdzielczości

Jak diagnozować opóźnienia wydajności

Do wykrywania przyczyn lagów używa się profilerów wbudowanych w IDE oraz narzędzi monitorowania systemowego. Każde narzędzie rozwiązuje swoje zadanie.

CPU Profiler w Android Studio

CPU Profiler pokazuje, które metody zajmują czas procesora i w których wątkach są wykonywane. Jeśli metoda z ciężkimi obliczeniami jest wykonywana w main thread — to źródło problemu. Nagranie śledzenia z włączonym sample Java Method pozwala zobaczyć stos wywołań w każdym momencie i znaleźć „gorące punkty”.

Time Profiler w Xcode Instruments

Analogiczne narzędzie dla iOS — Time Profiler — zbiera próbki stosu co milisekundę i pokazuje, jaki procent czasu CPU zajmuje każda metoda. Kombinacja z flagą Main Thread Only filtruje tylko operacje na głównym wątku, co bezpośrednio wskazuje źródła lagów.

Network Profiler i analiza zapytań

Wolne zapytania sieciowe stwarzają wrażenie lagów, nawet jeśli wątek UI nie jest zablokowany. Network Profiler w Android Studio i Network Link Conditioner w Xcode pozwalają symulować wolne połączenie i sprawdzić, jak aplikacja zachowuje się w rzeczywistych warunkach. Odpowiedzi chunkowane bez postępu i duże ładunki JSON to typowe źródła pozornych lagów.

Przykład profilowania zapytania sieciowego z OkHttp z pomiarem czasu:

kotlin
class TimingInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val start = System.nanoTime()
        val response = chain.proceed(chain.request())
        val duration = (System.nanoTime() - start) / 1_000_000
        Log.d("Czas", "Żądanie zajęło $duration ms")
        return response
    }
}

Metody eliminacji lagów na Android i iOS

Eliminacja lagów wymaga systematycznej pracy: od optymalizacji pojedynczej metody do zmian architektonicznych. Rozważmy najskuteczniejsze techniki.

Asynchroniczne przetwarzanie przez coroutines i GCD

Kotlin Coroutines z dyspozytorem Dispatchers.IO dla zapytań sieciowych i Dispatchers.Default dla obliczeń gwarantują, że główny wątek pozostaje wolny dla UI. Na iOS Grand Central Dispatch z queue .global(qos: .userInitiated) dla zadań tła i .main dla aktualizacji UI to standardowe podejście. Unikaj sync-operacji między kolejkami.

Optymalizacja adapterów i list

RecyclerView na Androidzie i UICollectionView na iOS wymagają prawidłowej konfiguracji: ViewHolder z minimalnym tworzeniem obiektów w onBindViewHolder, DiffUtil do obliczania zmian, prefetching do wcześniejszego ładowania danych. Na iOS używaj diffable data source do animowanych aktualizacji bez ręcznego zarządzania.

Buforowanie danych i obrazów

Ładowanie tego samego obrazu przy każdym przewijaniu to gwarantowany lag. Coil (Android) i Kingfisher (iOS) buforują obrazy w pamięci i na dysku, zapewniając natychmiastowe wyświetlanie przy ponownym zapytaniu. Do danych używaj Room z warstwą buforującą opartą na Flow lub Combine.

Przykład konfiguracji buforowania obrazów z Coil na Androidzie:

kotlin
val imageLoader = ImageLoader(context) {
    memoryCachePolicy(CachePolicy.ENABLED)
    diskCachePolicy(CachePolicy.ENABLED)
    crossfade(true)
    size(512, 512)
}

// Ładowanie z włączonym automatycznym buforowaniem
imageView.load("https://example.com/image.jpg") {
    placeholder(R.drawable.placeholder)
    error(R.drawable.error)
}

Profilaktyka lagów na etapie rozwoju

Zapobieganie lagom jest tańsze niż naprawianie ich w produkcji. Środki zapobiegawcze wbudowuje się w proces rozwoju na poziomie narzędzi i architektury.

StrictMode na Androidzie

StrictMode — wbudowane narzędzie Androida, które wykrywa przypadkowe operacje wejścia-wyjścia i wywołania sieciowe na głównym wątku na etapie rozwoju. Włącz je w Application.onCreate z polityką penaltyDeath dla krytycznych naruszeń. To jedyny sposób, aby zagwarantować, że programista zobaczy problem przed commitem.

Main Thread Checker na iOS

Analog dla iOS — Main Thread Checker w Xcode, wchodzący w skład Runtime Sanitization. Automatycznie sprawdza, czy wszystkie wywołania UIKit i AppKit są wykonywane z głównego wątku. Włącz go w schemacie kompilacji Debug i osiągnij zerowe ostrzeżenia w CI.

Benchmarki wydajności w CI

Dodaj do potoku CI uruchamianie Macrobenchmark (Android) i XCTMetrics (iOS) do pomiaru czasu uruchamiania, FPS przewijania i użycia pamięci. Ustaw progi: jeśli nowy commit zwiększa czas uruchamiania o więcej niż 5% — kompilacja upada.

  • Android — Macrobenchmark, Baseline Profiles, Jetpack Benchmark Library
  • iOS — XCTMetrics, os_signpost, MetricKit do zbierania metryk z urządzeń użytkowników
  • Ogólne podejście — profilowanie przed i po każdej znaczącej zmianie, regresyjne testy wydajności

Często zadawane pytania

Czym lag różni się od niskiego FPS?

Lag to subiektywne odczucie opóźnienia, które może występować nawet przy wysokim FPS, jeśli opóźnienie spowodowane jest czasem przetwarzania wejścia, a nie renderowania. Niski FPS (poniżej 30 klatek/s) — jedna z przyczyn lagów, ale nie jedyna.

Jak zmierzyć lag w aplikacji?

Użyj Frame Timing API na Androidzie (Choreographer) i CADisplayLink na iOS do pomiaru czasu między klatkami. Google Play Vitals pokazuje wskaźnik jank w rzeczywistych warunkach. Do dokładnych pomiarów używaj Macrobenchmark ze scenariuszami przewijania.

Dlaczego lagi pojawiają się tylko na starych urządzeniach?

Stare urządzenia mają mniej rdzeni CPU, mniejszą ilość RAM i wolniejszą pamięć. Operacja, która wykonuje się w 5 ms na flagowcu, na budżetowym urządzeniu może zająć 50 ms. Testuj wydajność na urządzeniach z niższej półki i ustaw Baseline Profiles dla AOT-kompilacji.

Czy optymalizacja obrazów może wyeliminować lagi?

Tak, to jeden z najskuteczniejszych sposobów. Obrazy wysokiej rozdzielczości zajmują dużo pamięci i czasu CPU na dekodowanie. Używaj downscale do rozmiaru View, formatów WebP (Android) i HEIC (iOS), a także buforowania przez Coil lub Kingfisher.

Jak SwiftUI wpływa na lagi w porównaniu z UIKit?

SwiftUI automatycznie optymalizuje aktualizacje przez diffing, co zmniejsza ryzyko lagów przy zmianie danych. Jednak złożone hierarchie i częste przebudowy body mogą powodować spadek FPS. UIKit daje większą kontrolę nad wydajnością, ale wymaga ręcznej optymalizacji.

Podsumowanie

  • Lag — opóźnienie między działaniem użytkownika a reakcją interfejsu, spowodowane problemami wydajności, a nie błędami logicznymi
  • Główne przyczyny — blokowanie głównego wątku, wycieki pamięci, ciężkie hierarchie layout i nieoptymalne zapytania sieciowe
  • Diagnostyka przeprowadzana przez CPU Profiler, Memory Profiler i Network Profiler na Androidzie; Time Profiler i Main Thread Checker na iOS
  • Eliminacja obejmuje coroutines, GCD, optymalizację adapterów, buforowanie obrazów danych i leniwe ładowanie
  • Profilaktyka — StrictMode, Macrobenchmark, Baseline Profiles, MetricKit i regresyjne testy wydajności
  • Pomiar — Choreographer na Androidzie, CADisplayLink na iOS, Google Play Vitals do monitorowania produkcji
  • Zalecenie: skonfiguruj CI ze sprawdzaniem FPS i czasu uruchamiania przy każdym commicie, aby zapobiegać regresjom

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ż