Android BroadcastReceiver as Flow

We all been to a position where we want to show a real-time message to the user when his internet connection has dropped. If not, we all have seen it somewhere.
There is this popular article solving the same problem with LiveData https://medium.com/android-news/connection-detection-using-livedata-android-623bd02b0e30. I wanted to tackle the same problem using Kotlin Flow.
I decided to make an extension function for Context
called networkBroadcastReceiverFlow
with a return type of Flow<Boolean>
.
fun Context.networkBroadcastReceiverFlow(): Flow<Boolean>
Moving on to the implementation, we need to define a callbackFlow
that will be used to emit the updates of network changes.
return callbackFlow {
}
In the callbackFlow
we want to register a BroadcastReceiver in order to get updates when the network state changes. So first, we need to create our BroadcastReceiver and register it to the system. The BroadcastReceiver should filter for the intended action, otherwise should do nothing and return.
val networkBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent.action != ConnectivityManager.CONNECTIVITY_ACTION) return
}
}val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
registerReceiver(networkBroadcastReceiver, filter)
Next step of the implementation is to actually check for an internet connection. From the intent
parameter we can get the NetworkInfo
object and emit connection status via the NetworkInfo.isConnected
or NetworkInfo.isConnectedOrConnecting
.
val activeNetwork = intent.extras?.get(ConnectivityManager.EXTRA_NETWORK_INFO) as NetworkInfo? ?: returnthis@callbackFlow.trySend(activeNetwork.isConnected)
I’m using the trySend
method to avoid blocking. You can use the send
method which suspends execution, and run the onReceive
code in a runBlocking
coroutine block.
After registering the receiver to the system, we need to find a way to unregister it so we don’t unnecessarily waste resources of the system. For this we can use the awaitClose
extension-function provided by the callbackFlow
.
awaitClose { this@networkBroadcastReceiverFlow.unregisterReceiver(networkBroadcastReceiver) }
And that’s pretty much it! Here’s the whole code described.
fun Context.networkBroadcastReceiverFlow(): Flow<Boolean> {
return callbackFlow {
val networkBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action != ConnectivityManager.CONNECTIVITY_ACTION) return
val activeNetwork = intent.extras?.get(ConnectivityManager.EXTRA_NETWORK_INFO) as NetworkInfo? ?: return
trySend(activeNetwork.isConnected)
}
}
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
registerReceiver(networkBroadcastReceiver, filter)
awaitClose { this@networkBroadcastReceiverFlow.unregisterReceiver(networkBroadcastReceiver) }
}
}
At this point you have noticed all the deprecation warnings. Even though the APIs used are marked as deprecated they seem to work just fine even on Android 12. You can use any new APIs Google introduced for network information to achieve the same goal. Even if they don’t work, this article is meant to showcase how we can make a Flow
out of a BroadcastReceiver
. After all, BroadcastReceiver
is a communication with the system using the callback
pattern. ¯\_(ツ)_/¯