Anslutningsförlust — ett av de vanligaste och mest irriterande fenomenen i mobila applikationer. Användaren förlorar åtkomst till data, operationen avbryts, applikationen fryser eller kraschar. Enligt Google Android Developer Blog tar 70% av användarna bort appen om den kraschar eller fryser två gånger. Låt oss analysera orsakerna till anslutningsförlust och sätt att bygga feltoleranta kommunikationer.
Huvudpunkter
Tappar anslutningen — en användarterm som beskriver situationen när appen förlorar anslutningen till servern, slutar svara på åtgärder eller avslutas med ett fel. I teknisk mening kan det vara: nätverksfel (timeout, DNS failure), ANR (blockering av UI-tråden), crash (ohanterat undantag) eller race condition (kapplöpningstillstånd).
För användaren ser alla dessa scenarier likadana ut: appen slutar fungera. Skillnaden för utvecklaren ligger i tillvägagångssättet för diagnos och reparation. Nätverksfel löses med retry-mekanismer, ANR — genom att flytta operationer utanför UI-tråden, crash — genom undantagshantering.
Enligt Crittercism (nu Apteligent) förlorar en genomsnittlig mobil app 1-2% av sina användare vid varje krasch. För en app med 1 miljon användare innebär detta 10-20 tusen förlorade installationer per bugg. Detta är särskilt kritiskt för appar inom finans- och medicinsektorn.
Instabilt nätverk — mobila enheter växlar ständigt mellan Wi-Fi och mobilt nätverk, går in i områden utan täckning (tunnelbana, hiss, källare). Varje växling orsakar tillfällig anslutningsförlust som appen måste hantera korrekt.
Timeoutar — om servern inte svarar inom den angivna tiden (vanligtvis 10-30 sekunder) kastar klienten SocketTimeoutException. Långa timeoutar utan återkoppling uppfattas av användaren som frysning. Det rekommenderas att ställa in timeout på högst 15 sekunder.
val client = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
Kapplöpningstillstånd (race condition) — uppstår när flera trådar samtidigt läser och skriver samma data utan synkronisering. Till exempel kan inläsning av data från cachen i UI-tråden parallellt med uppdatering av cachen från nätverket leda till visning av föråldrade eller felaktiga data.
Offline-first — arkitekturmönster där lokal lagring (Room, CoreData) är den enda sanningskällan. Nätverket används för datasynkronisering i bakgrunden. Användaren ser alltid aktuell data från den lokala cachen, även utan nätverk.
Repository pattern — en enda ingångspunkt för data som avgör om data ska hämtas från nätverket eller cachen. Repositoriet abstraherar datakällan från ViewModel och UI. Vid nätverksfel växlar repositoriet automatiskt till den lokala källan.
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) // returnera cache vid nätverksfel
} else {
Result.failure(e)
}
}
}
}
Circuit Breaker — mönster för att skydda servern från en översvämning av begäranden när den är otillgänglig. Efter N på varandra följande fel öppnas brytaren och alla begäranden returnerar omedelbart fel utan anslutningsförsök. Efter en angiven time-out går brytaren över till halvöppet tillstånd för en testbegäran.
Exponential backoff — standard retry-mekanism. Efter första misslyckandet vänta 1 sekund, efter andra — 2 sekunder, sedan 4, 8, 16. Begränsa maximalt antal försök (vanligtvis 3-5) för att inte överbelasta servern och batteriet.
Användaråterkoppling — vid nätverksfel visa ett begripligt meddelande: "Ingen anslutning", "Servern är tillfälligt otillgänglig", "Kontrollera internet". Använd Snackbar eller Inline State View. Visa aldrig tekniska fel (HTTP 500, SocketException) för användaren.
ConnectivityManager — Android API för nätverksövervakning. Låt appen reagera på förändringar: vid nätverksförlust visa en placeholder, vid återställning — uppdatera automatiskt data. I iOS använd NWPathMonitor från Network-ramverket.
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) — standardverktyg för krashrapportering för mobila appar. Samlar stacktrace för alla ohanterade undantag, OS-version, enhetsmodell och kraschens tidpunkt. Möjliggör gruppering av fel och tilldelning av ansvariga för åtgärder.
Sentry — alternativ till Crashlytics med stöd för prestandaövervakning. Möjliggör spårning av specifika transaktioner (t.ex. "användarautentisering") och att se vid vilket steg felet uppstod. Performance tracing hjälper att skilja nätverks-timeoutar från buggar i applogiken.
Timber — loggningsbibliotek för Android med automatisk tillägg av taggar per klass. I debug-bygge logga alla nätverksbegäranden och svar. I release-bygge — endast fel och varningar via Crashlytics.setCustomLog.
| Verktyg | Typ | När man ska använda |
|---|---|---|
| Crashlytics | Crash reporting | Alltid i release — automatisk insamling av krascher |
| Sentry | Crash + Performance | När du behöver profilera specifika användarscenarier |
| Timber | Logging | Debug: fullständig loggning; Release: endast fel |
| HTTP Toolkit | Network debug | Lokal avlyssning och analys av HTTP-trafik |
Enligt Firebase Summit 2023 minskar appar som implementerat Crashlytics + Performance Monitoring den genomsnittliga tiden för upptäckt och reparation av kritiska buggar från 3 dagar till 4 timmar. Det rekommenderas att ställa in larm för varje krasch med en frekvens högre än 0,1% av aktiva användare.
Vanliga frågor
Om kraschen inte fångas i Crashlytics, kontrollera native crash (SIGSEGV, SIGABRT) — de hanteras inte av Java/Kotlin exception handler. I Android kan detta vara native minnesläcka från JNI, i iOS — EXC_BAD_ACCESS. Använd Breakpad (Android) eller PLCrashReporter (iOS) för att samla in stacktrace för native krascher.
Använd Network Link Conditioner (inbyggt i iOS, för Android finns Facebook Network Connection Class eller inställningar Developer Options > Network > Select network type). Ställ in fördröjning på 500-3000 ms och paketförlust på 5-30%. Du kan också använda Charles Proxy eller mitmproxy för att emulera nätverksfördröjningar och avbrott.
ANR uppstår när UI-tråden blockeras längre än 5 sekunder. Nätverksbegäranden bör utföras i en bakgrundstråd: coroutines (viewModelScope.launch(Dispatchers.IO)), RxJava (subscribeOn(Schedulers.io)) eller WorkManager för synkronisering. Ställ alltid in timeoutar på HTTP-klienten — avsaknad av timeout kan leda till evig blockering.
Race condition — en situation där resultatet av en operation beror på ordningen i vilken trådar körs. Till exempel trycker användaren snabbt på knappen "Skicka" två gånger och begäran skickas två gånger. Lösning: använd Mutex, single-threaded executors eller state machine (inaktivera knappen efter första trycket). I Kotlin använd Mutex från coroutines eller @Synchronized-annoteringen.
Tillämpa Chaos Engineering för mobila appar: stäng av nätverket under operationer, simulera hög fördröjning, växla mellan Wi-Fi och mobilt nätverk, döda processen med systemet. Verktyg: Facebook Network Connection Class, Charles Proxy, iOS Network Link Conditioner. I CI/CD lägg till UI-tester med olika nätverksförhållanden via AndroidTest Orchestrator.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också