Network Connectivity Service using callbackFlow and Jetpack Compose. | by Saqib | May, 2023

How to observe Network connectivity status using Kotlin Flows and show it inside Compose UI?

Monitoring Network connectivity status is a very common use-case, either we want to display current network status on the UI or to show a retry mechanism if the device is offline.

This story will show code examples about how to monitor and convert Network connectivity callbacks into callbackFlow and observe it into the UI layer to show updates inside Compose UI and in the end it will provide helpful Takeaways .

Prerequisites

This story requires basic understanding of following components

  • Kotlin Flows and callbackFlow
  • Dependency Injection ( Dagger or Dagger Hilt)
  • Basic understanding of Jetpack Compose

What do we want to achieve?

We want to create a Network Connectivity Service to achieve followings

  • Monitor current Network Statuson device to know if device is connected to the Network or not.
  • Observe changes to the Network status in real time.
  • Use Jetpack Compose API to show offline status inside the Compose UI.

Let’s get to Network Connectivity Service.

NetworkStatus

As we want to read Network Status from the service. In order to expose Network Status from the service we will use a sealed class holding that status information. See below the NetworkStatus sealed class.

sealed class NetworkStatus 
object Unknown: NetworkStatus()
object Connected: NetworkStatus()
object Disconnected: NetworkStatus()

Network Connectivity Service

Let’s see the code first.

NetworkConnectivityServiceImpl is implementing an interface which is exposing networkStatus as Flow<NetworkStatus> .

The service is using callbackFlow , callbackFlow is an ideal choice here. Whenever we want to convert any API callbacks into Kotlin Flow callbackFlow is the answer. callbackFlow ensures that lambda remains active to be able to send data later-on during callbacks. It also provides awaitClose block to unregister listeners when callbackFlow closes.

In lambda connectivityManager is registering to the network status callbacks. In each callback method it sends a NetworkStatus using trySend . As mentioned before callbackFlow provides awaitClose block to unregister for the network changes. awaitClose will be called when callbackFlow is closed so we need to unregister for listeners.

flowOn(Dispatchers.IO) is to make sure that calling networkStatus is main safe because we will be calling it from the UI layer inside ViewModel.

distinctUntilChanged() ensures that it sends new value only when Network Status changes to avoid sending unnecessary updates. In real cases that will usually be the case when you turn off the Airplane mode then you will get the callback onAvailable twice, one for wifi and the other for cellular.

What to know more about callbackFlow? I have written a detailed story on callbackFlow , taking example of Firebase RealtimeDatabase callbacks and converting them into callbackFlow. If interested you can read from the link below.

Using Network Connectivity Service in ViewModel.

In order to provide current NetworkStatus for UI, we will create a networkStatus property in ViewModel from where Compose UI will collect it.

Exposing a property in ViewModel will look like below.

  • Flow.StateIn operator is used to convert callbackFlow into StateFlow .
  • NetworkConnectivityService will be injected as dependency inside the ViewModel constructor, I am using Hilt, It’s up to you whatever Dependency Injector you want to use.
  • initialValue inside Flow.StateIn is set Unknown because in the beginning when UI loads we will not know NetworkStatus and we don’t want to show any notification on UI setting default value to any of Connected or Disconnected.
  • Providing viewModelScope will bind Flow to the life-cycle of viewModelScope.
  • WhileSubscribed is used to cancel the upstream automatically when there are no collectors collecting the flow. WhileSubscribed(5000) will wait for 5 more minutes after the last collector before closing the upstream, It will avoid restarting the whole upstream flow unnecessary specially during configuration changes.

Collecting Network Status inside Compose UI

On the UI we will show notification when the device is offline using the snackbar. LaunchedEffect is the best choice here, I have written a very detailed blog post about LaunchedEffect vs rememberCoroutineScope exploring and explaining When and How to use both APIs. I recommend reading it, link is below.

Collecting networkStatus inside Compose UI looks like below.

  • collectAsStateWithLifecycle is used to collect for networkStatus in Composable, This is life-cycle-aware API and is a recommended way to collect Flow inside Compose.
  • LaunchedEffect is used to execute a suspend function snackbarHostState.showSnackbar("message") only when networkStatus is in Disconnected state. LaunchedEffect launches coroutine within the scope of the composable where it is being used and cancels it when it leaves the composition so we don’t need to worry about the life-cycle of coroutine being automatically managed.

Takeaways

  • callbackFlow is tailor built for converting any API callbacks into Kotlin Flow.
  • Flow.StateIn converts cold flow into hot flow, in this example it converts callbackFlow into StateFlow .
  • collect Flow in Jetpack Compose in a life-cycle-aware manner via API collectAsStateWithLifecycle
  • WhileSubscribed(5000) waits for 5 minutes before restarting upstream flow if flow does not have any observer, helpful in configuration changes or heavy upstream flows and also in UI related updates.
  • LaunchedEffect effect API is the recommended way to execute suspend functions as an effect of anything happening out of the scope of composable and changes of Network connectivity is an example of those side-effects happening out of the scope of composable.

Sources

That’s it for now! Hope it was helpful… Looking forward to any questions/suggestions in the comments.

Remember to follow and 👏 if you liked it 🙂

GitHub | LinkedIn | Twitter

Next Post

How Can Email Marketing Fuel Your Overall Inbound Strategy?

Email marketing fuels your overall inbound strategy by enabling targeted communication with your audience, nurturing leads, driving website traffic, and increasing customer engagement, ultimately resulting in higher conversions and a boosted return on investment (ROI). For every $1 spent on email marketing, $44 is made in return. That’s a 4400% […]
How Can Email Marketing Fuel Your Overall Inbound Strategy?