Listening for network state changes on Android

Denis Druzhinin
3 min readMar 22, 2021

It is not a secret that every network request could potentially fail for a multitude of reasons. The difficulty is that a failed network request results should be handled in different ways based on reasons they failed. For most other common network request errors, such as having code of a series of 500 HTTP error codes (server errors), it makes sense to apply a retry policy that implies several trials. In this article, I will discuss handling “No internet connection” errors in detail. In the next article, I will review retrying failed network requests automatically after a calculated timeout.

What is the reason for handling “No internet connection” errors separately? Well, if you simply retry a request in a cycle, one of two things will happen: consuming redundant battery energy or waiting for a long time when a connection is already available. If retry timeouts are relatively small and constant, then battery life would be affected significantly. On the other hand, if retry timeouts are relatively large or grow exponentially, an app probably will not retry its request for a long time after the moment when a connection becomes available again. We evidently would not be happy with both variants — consuming redundant battery energy or waiting for a long time when a connection is already available. For that reason, it makes sense to retry a request, which failed with some sort of “No internet connection” error, as soon as a network connection is available again.

NetworkStateManager interface

We are going to create a class, which encapsulates network connection state tracking and implements the following cute interface:

Note that networkState LiveData’s type parameter is not Boolean (connected / not connected), but has NetworkState type. In many situations, it is useful to know if only a cellular connection is available, or if a device is connected to a Wi-Fi network. In the case where only a cellular connection is available, this connection potentially can be metered and as a rule connection speed is slower compared to a Wi-Fi network. Therefore, an app can delay downloads and uploads of large files or ask a user to confirm that only cellular network is available. Nevertheless, it is easy to check if any connection is available with the help of NetworkState’s hasConnection property.

Using NetworkStateManager in a ViewModel

Having NetworkStateManager implementation, we could use it in our ViewModel as shown in the following code snippet:

ViewModel should not listen for events forever, that’s why we listen for networkStateManager.networkState only when ViewModel’s view is interested in the result. In case you are not familiar with MediatorLiveData, you can read an article devoted to LiveDatas, such as this one.

MyScreenModel’s startRequest() method tries to perform a network request and if it finishes with a network-related exception, isWaitingToRetryRequest is set to true.

NetworkUtils.isNoInternetException() method gives an ability to check a given throwable or its cause is an instance of UnknownHostException. I found that this simple check works well for the set of network related APIs, which are used in my current project. However, you should check that this method works for all of the network related APIs in your project as well.

Bring NetworkStateManager to life

In the first place, you have to declare ACCESS_NETWORK_STATE permission usage in app’s manifest:

<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE”/>

Now it’s time to implement NetworkStateManager interface.

NetworkStateManagerImpl class is not so cute and short as NetworkStateManager interface and it would be tiresome to copy this boilerplate code to every ViewModel that restarts requests based on a network state.

NetworkStateManagerImpl listens for changes in a list of available cellular and Wi-Fi networks with the help of the system ConnectivityManager class. When a new network is available, or an existing network is lost, NetworkStateManagerImpl updates availableCellularNetworkSet and availableWifiNetworkSet data structures and posts new connectionState value. Please note, that if both cellular and Wi-Fi networks are available, NetworkState.Wifi is chosen as a new connection state because usually Wi-Fi connection gives us more possibilities, as we discussed earlier.

NetworkStateManagerImpl should be instantiated as a Singleton. Otherwise, it would be necessary to unregister listeners, added to the system ConnectivityManager class instance when NetworkStateManagerImpl instance is no longer needed.

Slightly different implementation of NetworkStateManager is available in my GitHub repository. Let me know if you like AndroidBasicLib — I’ll upload it to Maven repository in order to make it possible to declare remote Gradle dependency to the library.

--

--