With the release of Android Q Preview at Google I/O 2019 this year I wanted to put together a tutorial on one of my favorite topics in mobile development, application networking. Specifically, in this tutorial, I will be covering how to detect network connectivity on an Android device from an application's perspective. This tutorial will act as the counterpart to the iOS tutorial on network connectivity I did earlier this year, but now with a few approaches from the Android development community. At the end of this tutorial you should know more about two common techniques used to check for network connectivity on Android applications, but if you run into any issues along the way, please feel free to leave a comment and I will address these items as soon as possible.
NOTE: The source code built for this tutorial was built on Android 9 with Android Studio 3.4.1 via Ubuntu 18.04. However, this should not affect the portability of this code. This tutorial does assume a working knowledge of Java and Android development, but nothing advanced.
Network Connectivity Model 🚀
In the Android ecosystem there are many different techniques used to detect connectivity depending upon the requirements of your application, but in this tutorial I will be covering just two of them. The first technique uses a ConnectivityManager to check the state of the network on the device, while the second technique checks the state of the error thrown if this error message is sent back in the response. Each technique has their pros and cons, but it’s worth taking a look at both of the options to see if they can be useful in your application. It's also worth noting that these techniques on Android do in some ways correlate to how connectivity is detected on iOS. For example, the second technique on exception detection in the response is similar to how iOS sends a network request and allows the URLSession stack to tell the application whether connectivity is available or not.
Creating a Main Activity for Connectivity Feedback 👨💻
Now that the network connectivity models have been discussed, let's take a look at how to implement these techniques. The first step will be to create a main activity to display the feedback for the state of your connectivity. To do this, start a new project in your favorite IDE (I'm using Android Studio) and take a look at the MainActivity.java and activity_main.xml files provided. These files will be the starting point for building the application user interface.
For this tutorial, building the user interface is out scope but I will provide a link to the layout file here. What I do want to cover is the code sitting behind the layout file in MainActivity.java. MainActivity.java is responsible for executing all of the interactions in the activity, and the following is a list of four main points in the MainActivity that help display the feedback loop for the connectivity state:
-
MainActivity's onCreate method. The onCreate() method that is called during the Activity lifecycle creation. In this example the MainActivity sets two TextViews by their layout id used in the layout file. These TextViews will be used while updating the view state during connectivity detection. For example, when the Request Data button is pressed, these TextViews are set with an initial state and after the connectivity is detected, they are updated with a final state.
-
Request Data Callback method. The requestDataCallback() method is used as a callback when the Request Data button is pressed. This action also initiates a AsyncTask to make a network call in the background and send updates to the UI thread and update the TextViews from (1).
-
Calling into the ConnectivityManager. The isNetworkReachable() method is called to check the state of the ConnectivityManager, or the connection on the device. This is the first technique that I mentioned from the network connectivity model above. This method uses the ConnectivityManager to check the connectivity on the device before the AsyncTask in (2) is called. If there is active connectivity then the AsyncTask is initiated. If not, then the interface from (4) is notified and the TextViews from (1) are updated accordingly.
-
Setting up an interface for the MainActivity. An interface for the Activity called MainActivityDataTaskNotification is setup and provides a callback for making updates to the Textviews. This allows for other classes to update the state of the MainActivity without creating a new instance. This interface is throughout the entire update life cycle.
import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.TextView; import android.net.ConnectivityManager; import android.net.NetworkInfo; // (4) Setting up an interface for the MainActivity interface MainActivityDataTaskNotification { void notifyMainActivity(String connStatus, String connInstruction); } public class MainActivity extends AppCompatActivity { // Activity declaration of textviews TextView connectivityStatus; TextView connectivityInstruction; @Override // (1) MainActivity's onCreate method protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Assignment of the Textviews from the user interface. (See activity_main.xml) connectivityStatus = findViewById(R.id.connectionStatus); connectivityInstruction = findViewById(R.id.requestContainerInstruction); } /** * (2) Request Data Callback method of the @+id/requestData button in activity_main.xml * * @param view : the view from the activity, in this case the button. * */ public void requestDataCallback(View view) { try { String connectionInstruction = ""; String connectionStatus = ""; if (isNetworkReachable()) { DataTask dataTask = new DataTask(mainActivityDataTaskNotification); connectionInstruction = dataTask.execute("https://httpbin.org/get?arg1=1&arg2=2").get(); connectionStatus = "Connection Status: Online"; } else { connectionInstruction = "Please check your internet connection and try your request again."; connectionStatus = "Connection Status: Offline"; } mainActivityDataTaskNotification.notifyMainActivity(connectionStatus, connectionInstruction); } catch (Exception e) { String connectionInstruction = e.getMessage(); String connectionStatus = ""; mainActivityDataTaskNotification.notifyMainActivity(connectionStatus, connectionInstruction); } } /** * (3) Calling into the ConnectivityManagerr : Class that answers queries about the state of network connectivity. * It also notifies applications when network connectivity changes. * @url https://developer.android.com/reference/android/net/ConnectivityManager * */ private boolean isNetworkReachable() { ConnectivityManager manager = (ConnectivityManager) getSystemService(this.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = manager.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { return true; } return false; } /** * (4) MainActivityDataTaskNotification (Closure) : Interface Method. * */ MainActivityDataTaskNotification mainActivityDataTaskNotification = new MainActivityDataTaskNotification() { @Override public void notifyMainActivity(String connStatus, String connInstruction) { connectivityInstruction.setText(connInstruction); connectivityStatus.setText(connStatus); } }; }
When executed the MainActivity and MainActivity layout will show the following state below when the application is loaded up. This is before any connectivity state is initiated or detected.
Creating a AsyncTask to send a Network Request 👨💻
As mentioned above in (2), the AsyncTask is used to make a network call once the ConnectivityManager gives the app the thumbs up that there is connectivity. From here the AsyncTask provides a set of methods that call back to the MainActivity to perform updates during the life cycle of the task. Below is a list of three main points used in this life cycle:
-
onPreExecute. The onPreExecute() method is invoked on the UI thread to send an initial update to the MainActivity before the background task executes. This update is set in the MainActivity's TextViews.
-
doInBackground. The doInBackground() method is invoked on a background thread and makes the network call. When the network has responded the file descriptor is read until a string is created as the response and this response is sent to onPostExecute.
-
onPostExecute. As mentioned in (2), the onPostExecute() method takes the network response as an argument and performs one last check for the "UnknownHostException." This can take place when the ConnectivityManager sends a false positive or the app loses connectivity when the network request leaves the app. Checking for the exception string is not the ideal state but is another technique that can be used from the connectivity model above.
package com.agnosticdev.networkconnectivity; import android.os.AsyncTask; import android.util.Log; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class DataTask extends AsyncTask<String, Void, String> { private MainActivityDataTaskNotification mainActivityInterface; public DataTask(MainActivityDataTaskNotification mainActivityInterface) { this.mainActivityInterface = mainActivityInterface; } @Override /** * (2) doInBackground * Notifies the main thread and calls the MainActivity method after onPreExecute is called. * <String, _, _> in the AsyncTask declaration. * @param String urlParams : passed into the execute() method. * */ public String doInBackground(String... urlParams) { String connectionInstruction = ""; String stringBufferData = ""; URL passedInURL; HttpURLConnection connection = null; try { if (urlParams.length > 0) { passedInURL = new URL(urlParams[0]); connection = (HttpURLConnection) passedInURL.openConnection(); InputStream fd = connection.getInputStream(); InputStreamReader fdRead = new InputStreamReader(fd); int buffer = fdRead.read(); while (buffer != -1) { char bufferedCharacter = (char) buffer; stringBufferData += bufferedCharacter; buffer = fdRead.read(); } } /// Do something meaningful with the stringBufferData here. /// For example, parse it into a model object. /// Meanwhile, onPostExecute to so this value can be passed back to the UI thread. connectionInstruction = "Based upon your last network request, your connectivity is good."; } catch (Exception e) { connectionInstruction = e.toString(); e.printStackTrace(); } return connectionInstruction; } @Override /** * (1) onPreExecute * Notifies the main thread and calls the MainActivity method before doInBackground is called. * <_, Void, _> in the AsyncTask declaration. * */ public void onPreExecute() { this.mainActivityInterface.notifyMainActivity("Connection Status: Checking", "Waiting for connection to finish"); } @Override /** * (3) onPostExecute * Returns the result from doInBackground and notifies the MainActivity on the main thread. * <_, _, String> in the AsyncTask declaration. * * @param String response : returned from the doInBackground method. */ public void onPostExecute(String response) { String status = "Online"; // Not a long term solution, but one technique could be to check for (UnknownHostException). if (response.contains("UnknownHostException")) { status = "Offline"; } this.mainActivityInterface.notifyMainActivity("Connection Status: " + status, response); } }
The image below is an example of the online and offline states set from the AsyncTask and notifying the MainActivity.
In Summary ⌛️
In summary, utilizing the ConnectivityManager or checking the response for the "UnknownHostException" message are just two common techniques that I have seen used to detect connectivity on Android. What works best for you will most likely be what works best for your application requirements and architecture, but I hope this tutorial can act as a guide to aide in techniques for application connectivity. I hope you have found this tutorial useful, and if you have any question or comments about this tutorial or code covered in it, please feel free to leave a comment and I will try and get back to you as soon as possible. Thank you very much!
References 📘
- Code from this tutorial is available here on my Github here.
- Android Connectivity Manager.
- Android AsyncTask.
- How to Detect Connectivity in iOS.
Comments
hello, is it possible to…
hello, is it possible to check the number of network bars?
Hello Sams,…
Hello Sams,
This is a great question. I sounds like what you are looking for is signal strength.
For Cellular signal strength, checkout this API: https://developer.android.com/reference/android/telephony/SignalStrength.html#getLevel()
For WiFi signal strength, it can be determined by the WiFi ScanResult API.
https://developer.android.com/reference/android/net/wifi/ScanResult.html#level
The Signal Strength is usually in dBm (decibels) and the closer to 0 the level is the better the signal.
Androidena.com
The description of the ConnectivityManager class tells me nothing about how to use the class, it s just a listing of method signatures and constant values. There s more info in the answers to this question than there is in that documentation. No mention of the fact that getActiveNetworkInfo() can return null, no mention of the need to have ACCESS_NETWORK_STATE permission, etc. Where exactly did you find this info, other than the Android source? dfjacobs Dec 27 '10 at 5:32