Network/Cellular data is widely used in Android Projects. Most of the app currently exist in the play store have API call some way or another. As in the nature of API (Application Programming Interface), you are interacting with another application that your project does not have any authority in the codebase. Sometimes, these APIs fail. And, when they fail, it is very important to show proper error message to the customers about what went wrong. Otherwise, the users will think that your app is not working properly.
One common problem that API might be failing due to the network availability. In case there is no cellular connection available in the user’s device, the API might fail. Since we as developers know that it does not make sense to use resources to launch a network call when there is no cellular/wifi connection available, a message can be prompted to the users regarding to the network availability. Thus, the customers can try to find a network connection and then start using your application.
In this article, we will try to cover how we can monitor, observe, or collect the network availability so that the user will not need to re-launch your app when they get a good connection. We will use
ConnectivityManager service, which is already provided in Android SDK.
Table of Contents
What is ConnectivityManager?
ConnectivityManager is a system service in Android platform. It is responsible to answer queries about network connectivity. ConnectivityManager allows us to monitor connectivity changes via using callbacks or Broadcast intents, and gives us specific details on the connection type (Cellular, WiFi, etc.). Also, it provides an API that allows applications request and select networks for their data traffic.
You can retrieve ConnectivityManager like;
1. Introducing Network Callback
The callback mechanism is a great way to observe network availability.
ConnectivityManager provides a clean architecture to implement it. Meet with
NetworkCallbacks. This class can be imagined as the lifecycle of the network connectivity. It allows us to know when the connectivity is available, when it is lost, and when a type of connectivity is changed. Some basic functions are down below. Please, visit at developer.android.com website for more details.
Called when the framework connects and has declared a new network ready for use.
|void||onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities)|
Called when the network corresponding to this request changes capabilities but still satisfies the requested criteria.
|void||onLosing(Network network, int maxMsToLive)|
Called when the network is about to be lost, typically because there are no outstanding requests left for it.
Called when a network disconnects or otherwise no longer satisfies this request or callback.
Called if no network is found within the timeout time specified in ConnectivityManager#requestNetwork call or if the requested network request cannot be fulfilled (whether or not a timeout was specified).
2. Creating Network Request (< API 24)
Network request establishes the contract on how and when we want to be notified regarding to connectivity changes. Network request allows us to set transport types that we are interested in observing. Note that this is not required API level 24 and above.
An example of how we can create network request.
3. Observing Network Availability
This final step will combine everything we learned so far. Now, observing network availability is much easier using network callbacks and network request. To do that, we need to wrap our NetworkCallbacks with Flow, or Observable depending on the preferred library. The code should look like below;
Ok. It might be quite a lot above. But let us break into smaller steps. First we create observable chain. (It is either
Observable.create respectively). Within the lambda function, we create our Network Callback instance, which will provide us network availability callbacks. Next big thing we need to consider is that, each callback we receive, we need to emit new item in our observing channel. We emit
onAvailable is called, and
onLost is called.
Now, we need to register our network request to
ConnectivityManager. We can achieve that through ConnectivityManager#requestNetwork. All there is left to pass both our
callback instances we created above. Notice that we are still in the lambda function of Flow or Observable constructor chain respectively.
Finally, as every environmental programmer should, we need to clear unused object when not needed. In this case, if collector/subscriber cancels or disposes the process, then we need to unregister the network callback so that we would not cause any memory leaks (For more info in memory leak, see this article). We can achieve that using ConnectivityManager#unregisterNetworkCallback, and we need to pass our
Please see more on default network request for API level 24+ in this article.
Now all there is to use connectivity state in our ViewModel. More than likely, the business logic will be not to call repository if there is no network available. So, the logic should look like;
You made it this far. For my readers speciality, I want to give you guys some bonus tips and tricks. If you test above code in a sample application, you should realize that
connectivityState will be keep changing
false when you turn on/off the WiFi or cellular data. This is great, that is actually what we wanted. However, did you test on opening the app without any network availability?
If you did so, you should realized that there would not be any emission.
NetworkCallback will not call
onLost if there was no network in the first place. Well, it makes sense, but what if your UI depends on this emission?
So, first and easier solution you can do is that, instead of
null default state in your UI, you can have initial UI state as
NetworkLostUiState. If there is no emission, your UI will show the correct state, instead of drawing nothing. Or, second best thing you can do is to use functional programming approach to have the first emission if there is no network available at the launch.
The logic should look like;
As you can see above, first we check if there is network available. If so, there should be emission coming up. Thus, if we send an emission here, it would be duplicated. However, if there is no network already, we start with
false emission so that we can provide the network not available even though there was no network to even start with.
In this article, we learn how to observe network availability. Now, we can listen any network changes and react and update the UI properly to those changes immediately so that the users will not need to take any action.
More posts are coming. Until then, stay tuned, and happy coding …