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).
NetResult netResult = null;
String url = "http://www.google.com/";
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("q", "bears"));
String serverResponse ="";
if (NetHandler.isNetworkConnected(con)) {
netResult = NetHandler.handlePostWithRetry
(url, params, NetHandler.NETWORK_DEFAULT_RETRY_ATTEMPTS);
serverResponse = Utils.removeUTF8BOM(EntityUtils.toString
(net_result.net_response_entity, Utils.CHARSET));
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:
String consume_str = Utils.removeUTF8BOM(EntityUtils.toString
(net_result.net_response_entity, Utils.CHARSET));
or if you want to capture JSON data:
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.
if (!AuthNetHandler.isAuthenticationSet()) {
AuthNetHandler.setAuthentication(host, new MyAuthenticationHelper(con));
NetAuthentication.loadUsernamePassword(username, password);
}
if (NetHandler.isNetworkConnected(context)) {
if (NetAuthentication.isReadyForLogin()) {
netResult = AuthNetHandler.handleAuthPostWithRetry(url, params, num_retries);
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.
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.
@Override
public void markAsLoginFailure(NetResult result) {
result.object = new String("Login Error");
}
or, in my case I use JSON objects:
@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.
@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.
@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.
@Override
public int validateLoginResponse(NetResult netResult) {
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
.
@Override
public int peekForNotLoggedInError(NetResult netResult) {
}
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.
@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);
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.