Ztráta spojení — jeden z nejčastějších a nejotravnějších jevů v mobilních aplikacích. Uživatel ztrácí přístup k datům, operace je přerušena, aplikace zamrzá nebo padá. Podle Google Android Developer Blog 70% uživatelů odstraňuje aplikaci, pokud dvakrát spadne nebo zamrzne. Rozebereme příčiny ztráty spojení a způsoby budování odolných komunikací.
Hlavní body
Odpadává — uživatelský termín popisující situaci, kdy aplikace ztrácí spojení se serverem, přestává reagovat na akce nebo končí chybou. V technickém smyslu to může být: síťová chyba (timeout, DNS failure), ANR (blokování vlákna UI), crash (neošetřená výjimka) nebo race condition (soutěžní stav).
Pro uživatele všechny tyto scénáře vypadají stejně: aplikace přestane fungovat. Rozdíl pro vývojáře spočívá v přístupu k diagnostice a opravě. Síťové chyby se řeší mechanismy retry, ANR — přenesením operací mimo vlákno UI, crash — ošetřením výjimek.
Podle Crittercism (nyní Apteligent) průměrná mobilní aplikace ztrácí 1-2% uživatelů při každém pádu. Pro aplikaci s 1 milionem uživatelů to znamená 10-20 tisíc ztracených instalací na jednu chybu. Zvláště kritické je to pro aplikace ve finančním a lékařském sektoru.
Nestabilní síť — mobilní zařízení neustále přepínají mezi Wi-Fi a mobilní sítí, vstupují do zón bez pokrytí (metro, výtah, sklep). Každé přepnutí způsobuje dočasnou ztrátu spojení, kterou musí aplikace správně zpracovat.
Časové limity — pokud server neodpoví ve stanoveném čase (obvykle 10-30 sekund), klient vyvolá SocketTimeoutException. Dlouhé časové limity bez zpětné vazby jsou uživatelem vnímány jako zamrznutí. Doporučuje se nastavit časový limit maximálně 15 sekund.
val client = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
Soutěžní stav (race condition) — vzniká, když několik vláken současně čte a zapisuje stejná data bez synchronizace. Například načítání dat z mezipaměti ve vlákně UI paralelně s aktualizací mezipaměti ze sítě může vést k zobrazení zastaralých nebo nesprávných dat.
Offline-first — architektonický vzor, kde lokální úložiště (Room, CoreData) je jediným zdrojem pravdy. Síť se používá pro synchronizaci dat na pozadí. Uživatel vždy vidí aktuální data z lokální mezipaměti, i při absenci sítě.
Repository pattern — jednotný vstupní bod pro data, který rozhoduje, zda data vzít ze sítě nebo z mezipaměti. Repozitář abstrahuje zdroj dat od ViewModel a UI. Při síťové chybě repozitář automaticky přepne na lokální zdroj.
class UserRepository(
private val api: UserApi,
private val dao: UserDao
) {
suspend fun getUsers(): Result<List<User>> {
return try {
val remote = api.fetchUsers()
dao.insertAll(remote)
Result.success(remote)
} catch (e: IOException) {
val cached = dao.getAll()
if (cached.isNotEmpty()) {
Result.success(cached) // vrátit mezipaměť při síťové chybě
} else {
Result.failure(e)
}
}
}
}
Circuit Breaker — vzor ochrany serveru před lavinou požadavků při nedostupnosti. Po N po sobě jdoucích chybách se vypínač otevře a všechny požadavky okamžitě vracejí chybu bez pokusu o připojení. Po stanoveném časovém limitu přejde vypínač do polootevřeného stavu pro zkušební požadavek.
Exponential backoff — standardní mechanismus retry. Po prvním neúspěchu počkejte 1 sekundu, po druhém — 2 sekundy, poté 4, 8, 16. Omezte maximální počet pokusů (obvykle 3-5), abyste nepřetěžovali server a baterii.
Zpětná vazba uživateli — při síťové chybě zobrazte srozumitelnou zprávu: „Žádné připojení”, „Server je dočasně nedostupný”, „Zkontrolujte internet”. Použijte Snackbar nebo Inline State View. Nikdy neukazujte uživateli technické chyby (HTTP 500, SocketException).
ConnectivityManager — Android API pro monitorování sítě. Umožněte aplikaci reagovat na změny: při ztrátě sítě zobrazte placeholder, při obnovení — automaticky aktualizujte data. V iOS použijte NWPathMonitor z frameworku Network.
class NetworkMonitor(private val context: Context) {
private val manager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
fun isOnline(): Boolean {
val network = manager.activeNetwork ?: return false
val caps = manager.getNetworkCapabilities(network) ?: return false
return caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}
}
Crashlytics (Firebase) — standardní nástroj pro hlášení pádů pro mobilní aplikace. Sbírá stacktrace všech neošetřených výjimek, verzi OS, model zařízení a čas pádu. Umožňuje seskupovat chyby a přiřazovat odpovědné osoby za opravy.
Sentry — alternativa k Crashlytics s podporou monitorování výkonu. Umožňuje trasovat konkrétní transakce (např. „autorizace uživatele”) a vidět, ve kterém kroku došlo k chybě. Performance tracing pomáhá odlišit síťové časové limity od chyb v logice aplikace.
Timber — knihovna pro logování pro Android s automatickým přidáváním značek podle třídy. V debug sestavení logujte všechny síťové požadavky a odpovědi. V release sestavení — pouze chyby a varování přes Crashlytics.setCustomLog.
| Nástroj | Typ | Kdy použít |
|---|---|---|
| Crashlytics | Crash reporting | Vždy v release — automatický sběr pádů |
| Sentry | Crash + Performance | Když potřebujete profilovat konkrétní uživatelské scénáře |
| Timber | Logging | Debug: plné logování; Release: pouze chyby |
| HTTP Toolkit | Network debug | Lokální zachycení a analýza HTTP provozu |
Podle Firebase Summit 2023 aplikace, které zavedly Crashlytics + Performance Monitoring, zkracují průměrnou dobu detekce a opravy kritických chyb ze 3 dnů na 4 hodiny. Doporučuje se nastavit výstrahy na každý pád s frekvencí vyšší než 0.1% aktivních uživatelů.
Často kladené otázky
Pokud crash není zachycen v Crashlytics, zkontrolujte native crash (SIGSEGV, SIGABRT) — ty nezpracovává Java/Kotlin exception handler. V Androidu to může být únik native paměti z JNI, v iOS — EXC_BAD_ACCESS. Použijte Breakpad (Android) nebo PLCrashReporter (iOS) pro sběr stacktrace native crash.
Použijte Network Link Conditioner (vestavěný v iOS, pro Android existuje Facebook Network Connection Class nebo nastavení Developer Options > Network > Select network type). Nastavte zpoždění 500-3000 ms a ztrátu paketů 5-30%. Také můžete použít Charles Proxy nebo mitmproxy pro emulaci síťových zpoždění a přerušení.
ANR vzniká, když je vlákno UI blokováno déle než 5 sekund. Síťové požadavky by měly být prováděny na vlákně na pozadí: coroutines (viewModelScope.launch(Dispatchers.IO)), RxJava (subscribeOn(Schedulers.io)) nebo WorkManager pro synchronizaci. Vždy nastavujte časové limity na HTTP klientovi — absence časového limitu může vést k věčnému blokování.
Race condition — situace, kdy výsledek operace závisí na pořadí provádění vláken. Například uživatel rychle dvakrát stiskne tlačítko „Odeslat” a požadavek je odeslán dvakrát. Řešení: použijte Mutex, single-threaded executors nebo state machine (deaktivujte tlačítko po prvním stisknutí). V Kotlin použijte Mutex z coroutines nebo anotaci @Synchronized.
Aplikujte Chaos Engineering pro mobilní aplikace: vypněte síť během operací, simulujte vysoké zpoždění, přepínejte mezi Wi-Fi a mobilní sítí, zabijte proces systémem. Nástroje: Facebook Network Connection Class, Charles Proxy, iOS Network Link Conditioner. V CI/CD přidejte UI testy s různými síťovými podmínkami přes AndroidTest Orchestrator.
Shrnutí
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í.
Přečtěte si také