Connection Loss — one of the most common and frustrating issues in mobile apps. The user loses access to data, an operation is interrupted, the app freezes or crashes. According to Google Android Developer Blog, 70% of users delete an app if it crashes or freezes twice. Let’s explore the reasons for connection loss and ways to build fault-tolerant communications.
Key Takeaways
Connection drop — a user term describing a situation when an app loses connection to the server, stops responding to actions, or terminates with an error. In technical terms, this can be: network error (timeout, DNS failure), ANR (UI thread freeze), crash (unhandled exception), or race condition.
From the user’s perspective, all these scenarios look the same: the app stops working. The difference for developers lies in the diagnostic and fix approach. Network errors are solved with retry mechanisms, ANR by offloading operations from the UI thread, crashes with exception handling.
According to Crittercism (now Apteligent), on average a mobile app loses 1–2% of users with each crash. For an app with 1 million users, this means 10–20 thousand lost installs per single bug. This is especially critical for apps in the financial and medical sectors.
Unstable network — mobile devices constantly switch between Wi-Fi and cellular networks, entering areas with no coverage (subway, elevator, basement). Each switch causes a temporary connection loss that the app must handle correctly.
Timeouts — if the server does not respond within the set timeout (usually 10–30 seconds), the client throws a SocketTimeoutException. Long timeouts without feedback are perceived by the user as freezing. It is recommended to set a timeout of no more than 15 seconds.
val client = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
Race condition — occurs when multiple threads read and write the same data simultaneously without synchronization. For example, loading data from cache in the UI thread while updating the cache from the network can lead to displaying outdated or incorrect data.
Offline-first — an architectural pattern where local storage (Room, CoreData) is the single source of truth. The network is used for background data synchronization. The user always sees up-to-date data from the local cache, even without a network connection.
Repository pattern — a single entry point for data that decides whether to fetch data from the network or the cache. The repository abstracts the data source from the ViewModel and UI. On network errors, the repository automatically switches to the local source.
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) // return cache on network error
} else {
Result.failure(e)
}
}
}
}
Circuit Breaker — a pattern that protects the server from a flood of requests when it’s unavailable. After N consecutive errors, the circuit breaker opens, and all requests immediately return an error without attempting a connection. After a specified timeout, the circuit breaker transitions to a half-open state for a test request.
Exponential backoff — a standard retry mechanism. After the first failure, wait 1 second; after the second, 2 seconds; then 4, 8, 16. Limit the maximum number of retries (usually 3–5) to avoid overloading the server and battery.
User feedback — on network errors, show a clear message: “No connection”, “Server temporarily unavailable”, “Check your internet”. Use Snackbar or Inline State View. Never show technical errors (HTTP 500, SocketException) to the user.
ConnectivityManager — Android API for network monitoring. Allow the app to react to changes: show a placeholder on connection loss, automatically refresh data on restoration. On iOS use NWPathMonitor from the Network framework.
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) — a standard crash-reporting tool for mobile apps. It collects stacktraces of all unhandled exceptions, OS version, device model, and crash time. It allows grouping errors and assigning responsible people for fixes.
Sentry — an alternative to Crashlytics with performance monitoring support. It allows tracing specific transactions (e.g., “user authorization”) and seeing at which step an error occurred. Performance tracing helps distinguish network timeouts from bugs in app logic.
Timber — a logging library for Android with automatic tag addition by class. In debug builds, log all network requests and responses. In release builds, log only errors and warnings via Crashlytics.setCustomLog.
| Tool | Type | When to Use |
|---|---|---|
| Crashlytics | Crash reporting | Always in release — automatic crash collection |
| Sentry | Crash + Performance | When you need to profile specific user scenarios |
| Timber | Logging | Debug: full logging; Release: errors only |
| HTTP Toolkit | Network debug | Local HTTP traffic interception and analysis |
According to Firebase Summit 2023, apps that implemented Crashlytics + Performance Monitoring reduce the average time to detect and fix critical bugs from 3 days to 4 hours. It is recommended to set up alerts for every crash with a frequency above 0.1% of active users.
Frequently Asked Questions
If a crash is not caught in Crashlytics, check for native crashes (SIGSEGV, SIGABRT) — they are not handled by the Java/Kotlin exception handler. In Android, this could be native memory leaks from JNI; in iOS, EXC_BAD_ACCESS. Use Breakpad (Android) or PLCrashReporter (iOS) to collect native crash stacktraces.
Use Network Link Conditioner (built into iOS; for Android, use Facebook Network Connection Class or Developer Options > Network > Select network type). Set a delay of 500–3000 ms and packet loss of 5–30%. You can also use Charles Proxy or mitmproxy to simulate network latency and disconnections.
ANR occurs if the UI thread is blocked for more than 5 seconds. Network requests should be executed on a background thread: coroutines (viewModelScope.launch(Dispatchers.IO)), RxJava (subscribeOn(Schedulers.io)), or WorkManager for synchronization. Always set timeouts on the HTTP client — a missing timeout can lead to permanent blocking.
Race condition — a situation where the outcome of an operation depends on the order of thread execution. For example, a user quickly presses the “Send” button twice, and the request is sent twice. Solution: use Mutex, single-threaded executors, or a state machine (disable the button after the first click). In Kotlin, use Mutex from coroutines or the @Synchronized annotation.
Apply Chaos Engineering for mobile apps: disconnect the network during operations, simulate high latency, switch between Wi-Fi and cellular, kill the process via the system. Tools: Facebook Network Connection Class, Charles Proxy, iOS Network Link Conditioner. In CI/CD, add UI tests with different network conditions via AndroidTest Orchestrator.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also