Handle Network Drops in Android Like a Pro: Build a Connectivity Observer with Flow (MVVM + Jetpack Compose )
Seamless network awareness in your app, the MVVM way.
Why This Matters
Modern mobile apps live and die by network connectivity.
Yet, Android’s ConnectivityManager.NetworkCallback
often misses reconnection events or fails silently on some devices. The result? Your app gets stuck “offline” even when the internet is back.
In this guide, we’ll show how to build a reliable, MVVM-aligned connectivity observer that’s:
-
Lifecycle-safe with
StateFlow
-
Jetpack Compose-compatible
-
Reactive using
callbackFlow
+ polling -
Completely decoupled via ViewModel
Architecture Breakdown
View (Compose UI)
↕
ViewModel (StateFlow)
↕
ConnectivityObserver (Flow of Status)
1. Define the ConnectivityObserver
Interface (Model Layer)
interface ConnectivityObserver {
enum class Status {
Available, Unavailable, Losing, Lost
}
fun observe(): Flow<Status>
}
2. Implement the NetworkConnectivityObserver
(Model Layer)
class NetworkConnectivityObserver(private val context: Context) : ConnectivityObserver {
private val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
override fun observe(): Flow<ConnectivityObserver.Status> = callbackFlow {
val callback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
trySend(ConnectivityObserver.Status.Available)
}
override fun onLost(network: Network) {
trySend(ConnectivityObserver.Status.Lost)
}
override fun onLosing(network: Network, maxMsToLive: Int) {
trySend(ConnectivityObserver.Status.Losing)
}
override fun onUnavailable() {
trySend(ConnectivityObserver.Status.Unavailable)
}
}
val request = NetworkRequest.Builder().build()
connectivityManager.registerNetworkCallback(request, callback)
// Periodic check to recover missed state
val scope = CoroutineScope(Dispatchers.IO)
scope.launch {
var lastStatus: ConnectivityObserver.Status? = null
while (isActive) {
val status = currentStatus()
if (status != lastStatus) {
trySend(status)
lastStatus = status
}
delay(3000)
}
}
awaitClose {
connectivityManager.unregisterNetworkCallback(callback)
}
}.distinctUntilChanged()
private fun currentStatus(): ConnectivityObserver.Status {
val info = connectivityManager.activeNetworkInfo
return if (info != null && info.isConnected) {
ConnectivityObserver.Status.Available
} else {
ConnectivityObserver.Status.Unavailable
}
}
}
3. Create the ViewModel (ViewModel Layer)
class NetworkViewModel(
private val connectivityObserver: ConnectivityObserver
) : ViewModel() {
val networkStatus: StateFlow<ConnectivityObserver.Status> =
connectivityObserver.observe()
.stateIn(viewModelScope, SharingStarted.Eagerly, ConnectivityObserver.Status.Available)
}
4. Hook it to Compose UI (View Layer)
@Composable
fun NetworkStatusScreen(viewModel: NetworkViewModel) {
val networkStatus by viewModel.networkStatus.collectAsState()
LaunchedEffect(networkStatus) {
when (networkStatus) {
ConnectivityObserver.Status.Available -> showSnackbar("You are online")
ConnectivityObserver.Status.Lost -> showSnackbar("You are offline")
ConnectivityObserver.Status.Losing -> showSnackbar("Connection is weak")
ConnectivityObserver.Status.Unavailable -> showSnackbar("No connection available")
}
}
// Your UI here...
}
5. Provide the ViewModel Without Hilt (Pure MVVM)
class NetworkViewModelFactory(
private val observer: ConnectivityObserver
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return NetworkViewModel(observer) as T
}
}
@Composable
fun AppScreen() {
val context = LocalContext.current
val observer = remember { NetworkConnectivityObserver(context) }
val viewModel: NetworkViewModel = viewModel(
factory = NetworkViewModelFactory(observer)
)
NetworkStatusScreen(viewModel)
}
Why This Approach is Better (The MVVM Way)
Traditional | MVVM Connectivity Observer |
---|---|
Imperative checks | Reactive Flow |
Tight coupling to UI | Decoupled via ViewModel |
Missed reconnect events | Periodic polling to fill the gaps |
Manual lifecycle control | Handled by ViewModel + Compose |
Difficult to test | Easy to unit test ViewModel behavior |
Final Words
By building a connectivity observer with Flow + MVVM + Compose, you future-proof your app’s network awareness. Your ViewModel becomes the single source of truth, and your UI stays reactive — always in sync with the network state.
Your users will never get stuck in “offline limbo” again.
Want More?
-
Add retry queues for failed actions
-
Persist last known status
-
Show persistent offline banners
-
Observe airplane mode changes too
Comments
Post a Comment