How to observe Internet in Android? A new way using Flow | by Kaushal Vasava | Nov, 2024

I will give you information about how to use Connectivity manager to listen network changes.

In Android development, managing network connectivity is crucial for ensuring your app behaves correctly when switching between networks or losing connection. One of the best ways to handle network changes is by using the ConnectivityManager. This class provides an easy way to monitor and react to changes in network status in real time. In this article, we’ll explore how to use NetworkCallback to detect network availability, handle network capabilities, such as internet access and observe network status in real time using Flow.

What is ConnectivityManager?

ConnectivityManager is a system service in Android that provides access to information about the network connectivity status of the device. It allows developers to monitor network connectivity and manage network-related operations like detecting if a network is available, if the device has internet access, and switching between different types of networks (e.g., Wi-Fi, mobile data).

With ConnectivityManager, you can:

  • Check the status of the network (whether it’s connected or not).
  • Monitor network types (Wi-Fi, cellular, Ethernet, etc.).
  • Register listeners (callbacks) to react to network changes.
  • Determine if the network connection is suitable for certain operations (e.g., whether it has internet access or is valid).

ConnectivityManager is commonly used when apps need to handle network connectivity changes or need to adapt their behaviour depending on the current network status.

We will create network observer in few steps.

private var connectivityManager: ConnectivityManager? =
context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager

We will use NetworkCallback of ConnectivityManager to observe connectivity changes using different callback methods.

What is NetworkCallback?

NetworkCallback is a class in the Android ConnectivityManager API that provides an easy way to monitor changes in network connectivity. By registering a NetworkCallback, you can listen for changes such as when a network becomes available, is lost, or has its capabilities changed (e.g., it gains internet access or loses validation).

Using NetworkCallback, your app can respond dynamically to network status changes and adapt accordingly, providing a seamless experience for users who may frequently switch networks, lose connection, or have limited connectivity.

There are several methods to observe network events.

Key Network Callback Methods:

NetworkCallback provides several methods to handle different network events:

  • onAvailable(Network network): Called when a network becomes available.
  • onLost(Network network): Called when a network is lost (e.g., the user switches to a different network).
  • onUnavailable(): Called when no network is available.
  • onCapabilitiesChanged(Network network, NetworkCapabilities capabilities): Called when the capabilities of a network change (e.g., the network gains or loses internet access or validation).
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
connectivityManager?.getNetworkCapabilities(network)?.let {
if (it.hasCapability(NET_CAPABILITY_INTERNET)) {
// trySend(true)
}
}
}

override fun onLost(network: Network) {
// trySend(false)
}

override fun onUnavailable() {
// trySend(false)
}

override fun onCapabilitiesChanged(
network: Network,
capabilities: NetworkCapabilities
) {
super.onCapabilitiesChanged(network, capabilities)
if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
// trySend(true)
} else {
// trySend(false)
}
}
}

We will now create network request using NetworkRequest Builder. This NetworkRequest is designed to request a network that meets certain capabilities and transport types, specifically focusing on networks that can provide internet access and support multiple types of network transport (Wi-Fi, Ethernet, and cellular).

val networkRequest = NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build()
  • NetworkRequest.Builder():

This creates an instance of NetworkRequest.Builder, which is used to specify the criteria for the type of network you want to request. You can add capabilities and transport types to the builder before finally building the NetworkRequest using the build() method.

  • addCapability(NET_CAPABILITY_INTERNET):

This adds a capability requirement for the network. In this case, NET_CAPABILITY_INTERNET means that the network must provide internet access. This ensures that the NetworkRequest will only match networks that are capable of providing internet connectivity (e.g., Wi-Fi, cellular, Ethernet with internet access).

This adds a transport type requirement for the network. The transport type refers to the medium or technology used for the network connection. By adding TRANSPORT_WIFI, TRANSPORT_ETHERNET, TRANSPORT_CELLULAR you’re telling the NetworkRequest that you are interested in Wi-Fi, Ethernet or Cellular networks. This would match networks using Wi-Fi, Ethernet, Cellular as the underlying technology.

To register network callback, we need to pass two params. First, which is networkRequest and another is networkCallback.

connectivityManager?.registerNetworkCallback(networkRequest, networkCallback)

Yeah! Our network observer is ready to use. but wait we have to add flow to send realtime network event changes.

So, we will use Flow API to send realtime changes. We use trySend method to send network event changes.

trySend():

The trySend() method is a Kotlin function commonly used in Coroutines to send values to a Channel or SharedFlow in a non-blocking manner. It is a suspending function used in conjunction with Kotlin Coroutines to emit values to a Channel or a SharedFlow without blocking the calling thread. If the value cannot be sent at that moment (for example, if the Channel is full or the flow is not ready to accept new values), it will return a Boolean indicating whether the value was successfully sent.

  • Non-blocking: Unlike the send() method, trySend() is non-blocking. If the operation cannot proceed immediately, it will return false instead of suspending the caller.
  • Return Value: It returns a ChannelResult (or Boolean in some versions of Kotlin) which is:
  • true: The value was successfully sent.
  • false: The value could not be sent at that moment.

We will send true when network is available else will send false.

val isConnectedFlow: Flow<Boolean>
get() = callbackFlow {
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
connectivityManager?.getNetworkCapabilities(network)?.let {
if (it.hasCapability(NET_CAPABILITY_INTERNET)) {
trySend(true)
}
}
}

override fun onLost(network: Network) {
trySend(false)
}

override fun onUnavailable() {
trySend(false)
}

override fun onCapabilitiesChanged(
network: Network,
capabilities: NetworkCapabilities
) {
super.onCapabilitiesChanged(network, capabilities)
if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
trySend(true)
} else {
trySend(false)
}
}
}
val networkRequest = NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build()

connectivityManager?.registerNetworkCallback(networkRequest, networkCallback)

awaitClose {
connectivityManager?.unregisterNetworkCallback(networkCallback)
}
}

How to use now?

Create viewmodel

class MainViewModel(networkObserver: NetworkObserver): ViewModel() {

val isConnected = networkObserver.isConnectedFlow.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000L),
initialValue = false
)
}

Observe isConnected in your view or compose.

val viewModel = viewModel<MainViewModel> {
MainViewModel(NetworkObserver(this@MainActivity))
}
val isConnected by viewModel.isConnected.collectAsState()

print("NetworkStatus: $isConnected") // isConnected is true = connected, false = not connected

Without or with ViewModel you can use it inside fragment or activity by initialise NetworkObserver class and pass context argument.

val networkObserver = NetworkObserver(context)

// Add below code in your onCreate() method of Activity or onViewCreated() method of fragment
viewLifecycleOwner.lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED){
networkObserver.isConnected.collectLatest {
print("NetworkStatus: $isConnected")
}
}
}

It’s Done.

Second way using lifecycleObserver. You can checkout this project.

Sample project:

Leave a Reply