Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Android Networking Example and Tutorial (with easy retries)

5.00/5 (4 votes)
19 Feb 2015CPOL4 min read 18.5K   1  
This post explains how to use the WorxForUs Network framework to have an Android app that works robustly even in areas that have poor network connectivity.

Get to know the WorxForUs network framework for Android. The same framework is used in the ChecklistsToGo app in the Google Play store.

Background

I wrote this library because none of the tutorials I found went clearly into the different types of faults that you can experience when developing web apps. They worked fine to get you started, but what about if you are now on a mobile network and not all of your packets are getting through? What happens if you socket times out? What do I do if I need to handle cookies? This framework is an attempt to address those issues along with adding a simple way to retry communications that did not go through properly.

Update

Just want to get started? Get the sample Eclipse project code.
NOTE: You will still need to download and link in the WorxForUs framework files as mentioned below.

Features

  • Automatic cookie handling
  • Baked in network retries (just specify number of retries to allow before failing)
  • Easy to understand handling of the various types of network errors
  • Handles authenticated and unauthenticated HTTP requests

Accessing the Network with Retries (Without Authentication)

First, download the WorxForUs framework from the Github page (or clone here). Import the project into your Eclipse or Android Studio.

Check to see if the network is connected (you don't want to try and download something if the user has activated flight mode).

Java
//NetResult is an object to store the results
NetResult netResult = null; 
String url = "http://www.google.com/";
//Load the values being posted 
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("q", "bears"));

String serverResponse ="";
//If the network is not currently connected, don't try to talk
if (NetHandler.isNetworkConnected(con)) {
        netResult = NetHandler.handlePostWithRetry
        (url, params, NetHandler.NETWORK_DEFAULT_RETRY_ATTEMPTS);
//get the server response as a string 
serverResponse = Utils.removeUTF8BOM(EntityUtils.toString
            (net_result.net_response_entity, Utils.CHARSET));
//Notify the HTTP client that all data was read
netResult.closeNetResult();
} 

What is this NetResult object? This object contains all the information you need to decode the response from the webserver. If netResult.net_response_entity is not null, then that is the response from the server. Send that value to your handling routing.

  • NetResult.net_success - Equals true when the server was successfully contacted (this says nothing about if your request was valid though).
  • NetResult.net_error - Contains the error message or exception associated with the connection
  • NetResult.net_error_type - Contains the name of the type of error that occurred (i.e., HttpResponseException, SocketTimeoutException, SocketException, or IOException)
  • NetResult.net_response_entity - This is the actual response from the server. A common use is to run:
Java
String consume_str = Utils.removeUTF8BOM(EntityUtils.toString
        (net_result.net_response_entity, Utils.CHARSET));

or if you want to capture JSON data:

Java
NetResult.handleGenericJsonResponseHelper(net_result, this.getClass().getName());

Note: The class name is passed for logging purposes only. Now net_result.object will contain your JSON objects parsed for you.

After reading your data from the netResult.net_response_entity, you will need to call netResult.closeNetResult(). This function eventually calls HttpEntity.consumeContent() which releases any resources associated with that object.

Accessing the Network with Authentication:

To call the network with authentication is simple once you have the authentication helper configured for your particular webserver.

Java
// load authentication data
if (!AuthNetHandler.isAuthenticationSet()) {
    // passing the context here allows the system to update the preferences 
    // with a validated usernumber (if found)
    AuthNetHandler.setAuthentication(host, new MyAuthenticationHelper(con));
    NetAuthentication.loadUsernamePassword(username, password);
}
// if network is ready, then continue
// check if network was disabled by the user

if (NetHandler.isNetworkConnected(context)) {
    // if user has credentials - upload then download so data is not lost
    if (NetAuthentication.isReadyForLogin()) {
        netResult = AuthNetHandler.handleAuthPostWithRetry(url, params, num_retries);
        //...handle the netResult response here
        netResult.closeNetResult();
    } else {
        Log.e(this.getClass().getName(), "Did not attempt to login, no authentication info");
    }
}

When you have authenticated requests, there are a few extra steps before you call AuthNetHandler.handleAuthPostWithRetry(...).

First, you will need to implement a class from NetAuthenticationHelper. This will tell the framework how you are going to login, what messages are errors, and basically how you are validating a login. It may seem like a lot of extra code, but try to stick with it. Having all your authentication code in one place can be extremely helpful.

Java
public class MyAuthHelper implements NetAuthenticationHelper {
    @Override
    public String getLoginURL(String host) {
        return "https://myweb/remote_login_address.php";
    }

getLoginURL is simply the location where you expect the app to connect to when sending login parameters. Just put the URL you need in here.

Java
@Override
    public void markAsLoginFailure(NetResult result) {
result.object = new String("Login Error");
    }

or, in my case I use JSON objects:

Java
@Override
    public void markAsLoginFailure(NetResult result) {
        try {
            result.object = new JSONObjectWrapper("Jsonstring");
        } catch (JSONExceptionWrapper e) {
            throw new RuntimeException("Could not parse default login failed JSON string.");
        }
    }

Put whatever your webserver responds with in here on a failed user login. This section forces a result to be simulated as a login failure in the result.object variable. Let's say you've identified a failed login, this output gets sent through validateLoginResponse(...) where the failed login will be identified.

Java
@Override
    public void markAsLoginSuccessFromCache(NetResult result) {
result.object = new String("Login Successful");
    }

Put whatever your webserver could respond with on a successful login. This section forces a result to be simulated as a login success in the result.object variable.

Java
@Override
    public String getLoginErrorMessage() { return "Could not login user"; }

Here, put what you would like to be passed as the error message to netResult.net_error when a user is not able to be logged in.

Java
@Override
    public int validateLoginResponse(NetResult netResult) {
        //Returns NetAuthentication.NO_ERRORS, NETWORK_ERROR, or SERVER_ERROR
        //Check the login server result from netResult.object
        //to determine if your login was successful or not
        if (netResult.net_success) {
            String response = netResult.object;
            if (response.contains("not logged in indication")) {
                return NetAuthentication.NOT_LOGGED_IN;
            } else if (response.contains("login error indication"))) {
                return NetAuthentication.LOGIN_FAILURE;
            } else if (response.contains("cant parse data indication"))) {
                return NetAuthentication.SERVER_ERROR;
            }
        } else {
            return NetAuthentication.NETWORK_ERROR;
        }
        return NetAuthentication.NO_ERRORS;    
    }

validateLoginResponse(...) performs the bulk of the login checking, it determines if the user wasn't logged in, there was a login error (i.e., wrong password), a server error, or network error. Depending on what you expect from the server, you will send a response of NetAuthentication.NO_ERRORS, NETWORK_ERROR, or SERVER_ERROR.

Java
@Override
    public int peekForNotLoggedInError(NetResult netResult) {
//... check the netResult.object for a login failure on a page that wasn't the login page.
    }

Similarly to the previous function, the peekForNotLoggedInError(...) checks for login errors, but on pages that are not the login page. Consider the example where you have already logged in, but then check a different page to download some other data. If your user's session is suddenly logged out, you will get an error that could look different than the one you get on the login page. So that specific logic for unexpected login failure goes in here.

Java
@Override
    public NetResult handleUsernameLogin(String host, String username, String password) {
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        params.add(new BasicNameValuePair("uname_field", "username"));
        params.add(new BasicNameValuePair("pword_field", "password"));
       
        NetResult netResult = NetHandler.handlePostWithRetry(this.getLoginURL(host), 
        params , NetHandler.NETWORK_DEFAULT_RETRY_ATTEMPTS);
        //save the result and close network stream
        consume_str = Utils.removeUTF8BOM(EntityUtils.toString
        (result.net_response_entity, Utils.CHARSET));
        netResult.object = consume_str;
        netResult.closeNetResult();
        return netResult;
    }

The handleUsernameLogin(...) function provides the actual fields and logic needed to send the request to the webserver. Simply fill in your specific fields for login.

If you have a different request using a token, the handleTokenLogin(...) function can be used for that purpose.

Wow, if you've made it to the end of this tutorial, you are a real trooper and I salute you!

Notes

HTTP Params are encoded with UTF-8. If your webserver expects another character set, you will need to change the handlePost(...) routing in NetHandler to use that encoding scheme.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)