Android HTTP Client : Network error and ConnectivityManager
In this post I will show how to handle connection errors or the situation when the connection changes (i.e from UMTS to WI-FI). In all these cases we have to gracefully handle these errors and implement some strategies to try to reconnect if possible.
In a perfect world when we connect to a remote server with HTTP protocol everything works perfectly, we don’t have to worry about connection errors, server under heavy request load or unstable network. In some of last posts we talked about how we can connect to a remote server and how we can send and receive data. In the real world, usually while we are connected to a remote server we walk or run or even we are in car, so that the network signal can vary or it can suddenly break. At the same time Android can device to switch from UMTS to WI-FI or vice versa or it can switch from one APN to another and so on. When we code our app, we have to take into account all these situation and handle them correctly. We can simplify and try to categorize all these events in two classes:
- Network error
- Network configuration changes
If we used Apache HTTP Client there’s already a mechanism that helps us, we could implement HttpRequestRetryHandler interface with our retry mechanism logic or a default implementation. If we use the standard Android HTTP connection we have to implement is by ourselves.
Handling connection error
In this post I showed how to connect to a remote server and we created an HttpClient class that manages the connection. In this case we extend this class handling possible network error. To do it we use the call back mechanism, in other word we set up a listener that gets notified when there is a connection error. To do it we create an interface:
public interface HttpConnectionRetryHandler { public boolean shouldRetry(Throwable t, int attemptNumber) ; }
This interface has just only one method where we implement our logic to handle or not the network error. We pass the exception and a counter that holds the number of attempt to re-establish the connection. The HttpClient then has to be modified to handle the connection error. It doesn’t have to raise an exception as soon as it can’t connect to the server but has to ask to our class that implements HttpConnectionRetryHandler if it should raise the exception or try to connect again.
public void connect(String method, Properties props) throws HttpClientException { boolean status = true; int attemptNumber = 0; while (status) { try { attemptNumber++; doConnection(method, props); status = false; } catch(Throwable t) { if (handler != null) { status = handler.shouldRetry(t, attemptNumber); if (!status) throw new HttpClientException(t); } else { throw new HttpClientException(t); } } } }
At line 12 when we have an exception we call handler.shouldRetry to verify if our connection error handler wants to handle the error or not. If not we raise the exception otherwise we keep on trying to reconnect. In our class that handles the error we implement our business logic. The simplest thing is checking the number of attempt. In this case we have:
public class DefaultHttpConnectionRetryHandler implements HttpConnectionRetryHandler { @Override public boolean shouldRetry(Throwable t, int attemptNumber) { System.out.println("Attempt ["+attemptNumber+"]"); if (attemptNumber > 5) return false; return true; } }
So when we call the HttpClient we have:
... client.setHandler(new DefaultHttpConnectionRetryHandler()); ...
Handling network configuration changes
As we said early while we move with our smartphone or tablet the network configuration can chance switching from WI-FI to UMTS or vice versa or from UTMS to switching from one APN to another (maybe in roaming). We have to handle all these changes and try to use the network configurations to re-establish the connection. To handle these events we can use a Broadcast receiver that gets notified when a network configuration change occurs. Changes in APN configuration or in changes in connectivity is handle by ConnectivityManager that broadcast some information to its subscribers. So the first thing we code our broadcast receiver:
public class ConnectionMonitorReceiver extends BroadcastReceiver { @Override public void onReceive(Context ctx, Intent intent) { System.out.println("Receive.."); NetworkInfo netInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); if (netInfo == null) return ; System.out.println("Here"); if (netInfo.getType() == ConnectivityManager.TYPE_MOBILE) { String proxyHost = System.getProperty( "http.proxyHost" ); String proxyPort = System.getProperty( "http.proxyPort" ); if (proxyHost != null) HttpClient.getInstance().setProxy(proxyHost, proxyPort); else HttpClient.getInstance().setProxy(null, null); } } }
At line 1 we simply extends the BroadcastReceiver and we implement onReceive method (line 4). At line 6 we get the information shipped inside the intent broad and implements our logic. In our case we simply check if the proxy is changed but we could implements other type of business logic.
The last thing we have to do is register our broadcast receiver so that it can be called. We do it in our onCreate method of the Activity:
registerReceiver(new ConnectionMonitorReceiver(), new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
Running the code we have:
Can I use this concept to connect my app with some other external wifi devices.?
im planning to do a home automation project ,
when i click button on android pp, it should activate the targeted device ,
Can u please help me how can i implement
onClickListener, onClick methods using a button ?
Hello Francesco, can you post the source code of the app that you made for this example, it would very nice, thx and great post :)
Greetings from Chile :)