Zamrzání ve vývoji — podstata, příčiny a prevence

Autor: IT Sectr Publikováno: 2026-07-28 Doba čtení: 9 min

Zamrzání (visnet) — je stav, kdy mobilní aplikace přestane na delší dobu reagovat na jakékoli akce uživatele. Na rozdíl od lagů (zpomalení) a glitchů (nesprávné chování) zamrzání zcela blokuje UI: dotyky nejsou zpracovávány, animace se zastaví, obrazovka „zamrzne". Příčinou je blokování hlavního vlákna synchronní operací, deadlock ve vícevláknovém kódu nebo anomálně dlouhé garbage collection. Podle Apple Main Thread Checker Documentation souvisí více než 40 % crash hlášení na iOS s blokováním hlavního vlákna. Na Androidu vede obdobná situace k ANR — systémovému dialogu „Aplikace neodpovídá".

Hlavní body

  • Zamrzání — úplné blokování UI na delší dobu (sekundy a desítky sekund), lišící se od lagů a glitchů
  • Hlavní příčiny — blokování hlavního vlákna vstupně-výstupní operací, deadlock mezi vlákny, nekonečná smyčka a únik paměti s dlouhým GC
  • Diagnostika zahrnuje Main Thread Checker na iOS, ANR logy /data/anr/traces.txt na Androidu a analýzu dumpů vláken
  • Odstranění — přesun všech potenciálně dlouhých operací do vláken na pozadí, použití Structured Concurrency a vyhýbání se synchronized ve vlákně UI
  • Prevence — StrictMode, Main Thread Checker v Debug schématu, statická analýza na deadlock a pravidelné spouštění testů s měřením doby odezvy

Co je zamrzání v mobilním vývoji

Zamrzání (freeze, hang) v mobilní aplikaci — je stav, kdy aplikace přestane zpracovávat vstupní události a aktualizovat rozhraní po dobu několika sekund nebo déle. Technicky to znamená, že hlavní vlákno (main thread) je blokováno a nemůže provést další cyklus runneru.

Rozdíl mezi zamrzáním, lagem a ANR

Lag — zpoždění do 500 ms, při kterém uživatel zaznamená zpomalení, ale aplikace dále funguje. Zamrzání trvá od 1 sekundy do desítek sekund. ANR na Androidu — je zvláštní případ zamrzání, které trvalo déle než 5 sekund a bylo detekováno systémem. Ne každé zamrzání vede k ANR, ale každý ANR je systémem zdokumentované zamrzání.

Důsledky zamrzání

Na Androidu zamrzání delší než 5 sekund vyvolá ANR dialog s návrhem na zavření aplikace. Na iOS má systém watchdog — pokud aplikace nereaguje na události do 10–20 sekund, Watchdog ukončí proces kódem 0x8badf00d (ate bad food). Uživatel vidí pouze náhlé zavření aplikace a návrat na hlavní obrazovku.

Příčiny zamrzání na Androidu a iOS

Každá operace, která trvá déle než 100 ms a je spuštěna v hlavním vlákně, potenciálně způsobuje zamrzání. Podívejme se na hlavní zdroje blokování.

Synchronní vstup-výstup ve vlákně UI

Čtení velkého souboru, síťový požadavek bez asynchronnosti, ukládání dat do SharedPreferences synchronní metodou apply následovanou commit — všechny tyto operace blokují hlavní vlákno. Na Androidu může synchronní čtení souboru o velikosti 10 MB trvat 200–500 ms v závislosti na rychlosti flash paměti. Na iOS synchronní načítání URLSession bez completionHandler blokuje UI po dobu odezvy serveru.

Deadlock ve vícevláknovém kódu

Když dvě vlákna čekají na uvolnění prostředků, které drží navzájem, vzniká deadlock. V mobilních aplikacích typický scénář — vlákno A blokuje Lock1 a čeká na Lock2, zatímco vlákno B blokuje Lock2 a čeká na Lock1. Obě vlákna zamrznou navždy. Pokud je jedním z nich hlavní vlákno, aplikace zcela zamrzne.

Nekonečná smyčka nebo rekurze

Chyba v logice — například while(true) bez podmínky ukončení nebo rekurze bez základního případu — vede k nekonečnému provádění na hlavním vlákně. Android to detekuje prostřednictvím ANR po 5 sekundách, iOS — prostřednictvím Stackshot, který zaznamenává nekonečně se opakující zásobník volání.

  • Android — Cursor bez uzavření, synchronní požadavek přes execute() místo enqueue(), FileInputStream.read() ve vlákně UI
  • iOS — performSelectorOnMainThread:withObject:waitUntilDone:YES, spuštění NSURLConnection sendSynchronousRequest, načítání obrázku s dataWithContentsOfURL
  • Cross-platform — Flutter compute bez vyhrazeného isolate, React Native synchronní NativeModule

Jak diagnostikovat zamrzání

Diagnostika zamrzání vyžaduje nástroje schopné zaznamenat stav všech vláken v okamžiku blokování.

ANR logy na Androidu

Při každém ANR systém Android ukládá soubor /data/anr/traces.txt, který obsahuje dump zásobníku každého vlákna aplikace. Analýza tohoto souboru — hlavní diagnostická metoda: je třeba najít vlákno main a zjistit, na které metodě se zastavilo. Pokud zásobník končí Thread.sleep, InputStream.read nebo Lock.lock — příčina je nalezena.

Stackshot na iOS

Xcode při zamrznutí aplikace (signál SIGSTOP) může pořídit Stackshot — snímek zásobníků všech vláken. Zapněte ve schématu „Logging" → „Include Stackshot Logs". Při pádu s kódem 0x8badf00d extrahujte crash log z Devices & Simulators a najděte vlákno com.apple.main-thread se zamrzlým zásobníkem.

Main Thread Checker v Xcode

Main Thread Checker automaticky detekuje volání UIKit z vláken na pozadí během provozu aplikace. Zapněte jej ve schématu (Diagnostics → Main Thread Checker). Každé varování je potenciální příčinou zamrzání, zejména pokud se vyskytuje v uzavření completionHandler síťového požadavku.

Příklad detekce blokování pomocí StrictMode na Androidu:

kotlin
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
            .detectLeakedSqlLiteObjects()
            .detectLeakedClosableObjects()
            .penaltyLog()
            .build())
        StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
            .detectAll()
            .penaltyDeath()
            .build())
    }
}

Metody odstranění blokování UI

Odstranění zamrzání začíná přesunem všech potenciálně dlouhých operací do vláken na pozadí. Podívejme se na konkrétní techniky pro každou platformu.

Structured Concurrency s korutinami

Kotlin Coroutines s viewModelScope.launch(Dispatchers.IO) zaručují, že síťová operace nebo čtení z databáze probíhá ve vlákně na pozadí. Dispatchers.Main se používá pouze pro aktualizaci UI. Důležité: všechny suspend funkce musí být strukturované — podřízené korutiny se ruší při zrušení rodiče, čímž se zabraňuje úniku vláken.

Asynchronní fronty na iOS

Grand Central Dispatch s DispatchQueue.global(qos: .userInitiated) pro úlohy na pozadí a DispatchQueue.main.async pro aktualizaci UI — standardní vzor. Vyhněte se sync() na hlavní frontě — to je zaručený deadlock. Používejte async/await (Swift 5.5+) pro čitelnější asynchronní kód s automatickým návratem do hlavního vlákna přes MainActor.

Vyhýbání se synchronized ve vlákně UI

Bloky synchronized v Kotlin a @synchronized ve Swift na hlavním vlákně jsou nebezpečné: pokud jiné vlákno již toto zamykání získalo, hlavní vlákno zamrzne v čekání. Používejte atomické typy (AtomicInteger, atomické vlastnosti ve Swift) nebo sekvenční fronty místo zámků.

Příklad asynchronního načítání dat s korutinami na Androidu:

kotlin
class DataViewModel : ViewModel() {
    private val _data = MutableStateFlow<List<Item>>(emptyList())
    val data: StateFlow<List<Item>> = _data.asStateFlow()

    fun loadData() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = fetchFromNetwork()
            _data.emit(result)
        }
    }
}

Prevence zamrzání ve fázi vývoje

Systematické předcházení zamrzání pomáhá kombinace nástrojů, architektonických principů a procesů code review.

StrictMode s penaltyDeath

Nastavte StrictMode s penaltyDeath pro politiky vláken — to povede k okamžitému pádu aplikace při detekci síťového volání nebo diskového I/O v hlavním vlákně. Vývojář nebude moci problém ignorovat. V produkčním sestavení používejte penaltyLog pro sběr statistik bez pádů.

Main Thread Checker v Debug schématu

Na iOS zapněte Main Thread Checker v Debug schématu a nastavte CI na spouštění testů s touto možností. Pokud test obsahuje volání UIKit z vlákna na pozadí — musí selhat. To je jediný spolehlivý způsob, jak odhalit problém před odesláním do TestFlight.

Code review s kontrolou vícevláknovosti

Přidejte do procesu code review povinný bod: kontrola, že každé síťové volání, práce se soubory, databází nebo těžké výpočty probíhají ve vlákně na pozadí. Deadlock lze detekovat statickým analyzátorem: Infer od Facebooku a Thread Safety Checker od Xcode najdou potenciální blokování před spuštěním.

  • Android — StrictMode, Infer, Android Lint Multithread, Kotlin Coroutines s viewModelScope
  • iOS — Main Thread Checker, TSAN (Thread Sanitizer), Xcode Analyze, Swift async/await s MainActor
  • Cross-platform — Flutter compute isolate, React Native interaction manager s requestAnimationFrame

Často kladené otázky

Jaký je rozdíl mezi zamrzáním a ANR?

ANR (Application Not Responding) — systémové oznámení Androidu, které se objeví při zamrznutí hlavního vlákna déle než 5 sekund. Zamrzání je širší pojem: jakékoli blokování UI libovolné délky. Na iOS neexistuje ANR, ale existuje Watchdog s timeoutem 10–20 sekund.

Jak číst traces.txt na Androidu?

Soubor se nachází v /data/anr/traces.txt. Pro přístup je vyžadován root nebo adb shell: proveďte adb shell cat /data/anr/traces.txt \> traces.txt s root oprávněními. V zásobníku najděte vlákno „main" — poslední volaná metoda ukazuje na příčinu blokování.

Proč aplikace zamrzá na iOS, ale nepadá?

Pokud zamrzání trvá méně než 10 sekund, Watchdog se neaktivuje a aplikace prostě „zamrzne" do dokončení blokující operace. Uživatel nevidí pád, ale zažívá frustraci. Pro detekci takových případů použijte MetricKit s vlastním sledováním doby provádění.

Jak testovat aplikaci na zamrzání?

Používejte UI testy s kontrolou, že se obrazovka otevře za méně než 1 sekundu. Přidejte do CI měření času mezi dotykem a objevením další obrazovky. Na Androidu používejte Espresso s IdlingResource pro čekání na asynchronní operace. Na iOS XCTest s XCTWaiter pro kontrolu doby načítání.

Může SwiftUI způsobit zamrzání?

SwiftUI samo o sobě nezpůsobuje zamrzání, ale složité výpočty ve vlastnosti body — ano. Pokud se body počítá 500 ms kvůli těžkým operacím, UI zamrzne. Řešení — přesuňte výpočty do Task.detached a aktualizujte @State asynchronně na hlavním actorovi.

Shrnutí

  • Zamrzání — úplné blokování UI na sekundy a desítky sekund, způsobené blokováním hlavního vlákna, deadlockem nebo nekonečnou smyčkou
  • Diagnostika — /data/anr/traces.txt na Androidu, Stackshot a Main Thread Checker na iOS
  • Hlavní příčiny — synchronní I/O, deadlock mezi vlákny, nekonečná rekurze, dlouhý GC
  • Odstranění — korutiny se správnými dispečery, async/await s MainActor, přesun všech I/O operací do vláken na pozadí
  • Prevence — StrictMode s penaltyDeath, Main Thread Checker, statická analýza deadlocku (Infer, TSAN)
  • Na Androidu zamrzání > 5 s = ANR; na iOS > 10–20 s = Watchdog crash (0x8badf00d)
  • Doporučení: zapněte Thread Sanitizer v Debug schématu a nastavte CI na spouštění testů s TSAN pro detekci data race a deadlock

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také