- Provides a simple interface for making HTTP requests asynchronously
- Allows client complete control over request and response handling
- Optionally queues failed (no response) requests to be run in the future
- Client sets frequency to retry
- Works in background service, so requests can be made when the app is not running
- Queue is stored persistently
- Background service can be started when device boots
- Download this project and add it to your project's build path.
- If you haven't already created an
Application
subclass, create one that has at least this in theonCreate()
method (more on this below in the Other sample code section):PendingWebServiceQueueApplicationHelper.getInstance(this);
- Register your
Application
subclass in the manifest by adding the name of yourApplication
subclass as an attribute to theapplication
node. For example:android:name=".MyApplication"
Queued HTTP requests are run on a regular interval, specified by you, after the failure in a background service. If you'd like queued HTTP requests to be run when the device reboots, as opposed to the next time the app starts after a reboot, create a BroadcastReceiver
subclass, and register the BroadcastReceiver
in the manifest. You can find more on this below in the Other sample code section.
This library includes classes to handle GET, POST, PUT, and DELETE requests. The classes are:
WebServiceGetAbstract
WebServicePostAbstract
WebServicePutAbstract
WebServiceDeleteAbstract
All four of those inherit from WebServiceAbstract
. So you'll extend one of those four classes, depending on which HTTP method you need.
Here's a class which makes a GET request to the Yahoo Finance API for stock quotes:
public class GetYahooFinanceQuote extends WebServiceGetAbstract
{
/**
* This property is implementation-specific,
* used to hold the stock symbol to be used by getUri()
*/
protected String symbol;
public GetYahooFinanceQuote(Context context)
{
super(context);
}
/**
* This method is implementation-specific,
* used to set the stock symbol to be used by getUri()
*/
public void setStockSymbol(String symbol)
{
this.symbol = symbol;
}
protected String getDomain()
{
return "query.yahooapis.com";
}
protected String getUri()
{
String uri = "/v1/public/yql";
String yql = "select * from yahoo.finance.quote where symbol = '" + symbol + "'";
try {
yql = URLEncoder.encode(yql, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String queryString = "?q=" + yql + "&format=json&diagnostics=false&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys";
return uri + queryString;
}
protected boolean canMakeRequest()
{
return (symbol != null);
}
}
As you can see, you simply inherit from WebServiceGetAbstract
and implement the specifics, which are often very simple.
To make the request and handle the response, you'll do something like this:
GetYahooFinanceQuote api = new GetYahooFinanceQuote(this);
api.setStockSymbol(secFiling.getIssuer().getTradingSymbol());
AsyncTaskResponseHandlerInterface responseHandler = new AsyncTaskResponseHandlerInterface() {
public void handleResponse(RestResponseInterface response)
{
handleGetFinanceQuoteServiceResponse(view, response);
}
};
api.makeRequest(responseHandler);
There are four things happening in the above snippet:
- Instantiate your class that extends
WebServiceAbstract
. - Set any implementation-specific values, such as the trading symbol in the example.
- Define and create an instance of
AsyncTaskResponseHandlerInterface
which implements thehandleResponse
method. - Make the request and pass in your
AsyncTaskResponseHandlerInterface
.
Here's a class which makes a POST request:
public class PostAccountSettings extends WebServicePostAbstract
{
/**
* This property is implementation-specific,
* used to hold values for the post body
*/
Map<String, String> values;
public PostAccountSettings(Context context)
{
super(context);
}
/**
* This method is implementation-specific,
* used to set values for the post body
*/
public void setValues(Map<String, String> values)
{
this.values = values;
}
/**
* Override default implementation so that this request can be queued
*/
public String serializeValues()
{
Gson gson = new Gson();
String encoded = gson.toJson(values);
return encoded;
}
/**
* Override default implementation so that this request can be queued
*/
public void unserializeValues(String values)
{
Gson gson = new Gson();
Type type = new TypeToken<Map<String, String>>() {}.getType();
Map<String, String> decoded = gson.fromJson(values, type);
this.values = decoded;
}
/**
* Override default implementation, which is simpy an MD5 of class name
* This is used for the identifier when the serialized request is stored
* In this case, I can have more than one request by this class queued,
* so I want to differentiate by the keys of the values being posted
*/
public String getId()
{
StringBuilder builder = new StringBuilder();
builder.append(getUri());
Set<String> keys = values.keySet();
for (String key : keys) {
builder.append(key);
}
return md5(builder.toString());
}
protected String getPostBody()
{
return serializeValues();
}
protected String getDomain()
{
return Config.DOMAIN;
}
protected String getUri()
{
return Config.POST_ACCOUNT_SETTINGS;
}
protected boolean canMakeRequest()
{
return (values != null);
}
}
Note the methods, which were not present in the GET example above. getPostBody()
is required by the WebServicePostAbstract
class. The other methods, serializeValues()
, unserializeValues()
, and getId()
, are to support queueing the request in case of failure. getId()
is optinonal and is only overridden if you want to queue multiple requests of the same class.
To make the request, handle the response, and queue the request if it fails, you'll do something like this:
PostAccountSettings api = getApi();
Map<String, String> values = new HashMap<String, String>();
values.put(NOTIFICATION_THRESHOLD_DOLLAR_AMOUNT, String.valueOf(value));
api.setValues(values);
AsyncTaskResponseHandlerInterface responseHandler = new AsyncTaskResponseHandlerInterface() {
public void handleResponse(RestResponseInterface response)
{
handleAccountSettingsPostResponse(response);
}
};
PendingWebServiceQueueParams webServiceQueueParams = new PendingWebServiceQueueParams();
try {
webServiceQueueParams.setFrequency(30, Calendar.SECOND);
} catch (Exception e) {
e.printStackTrace();
}
api.makeRequest(responseHandler, webServiceQueueParams);
This is almost the same as in the GET example above, except that we pass PendingWebServiceQueueParams
to the makeRequest()
method. This is all that it takes to tell the application to queue the request if it fails. Upon failure, the app will make the request every 30 seconds (or whatever your parameters are) until it succeeds. This happens even if your app isn't in the foreground, and it can persist across device reboots.
The response comes packaged in a RestResponseInterface
object, which has two self-explanatory methods:
public int getStatusCode()
public String getResponseBody()
This object is passed to the AsyncTaskResponseHandlerInterface.handleResponse()
method which you implemented. The rest is up to you!
The thing of importance is the line PendingWebServiceQueueApplicationHelper.getInstance(this);
. This starts the background service when your app starts, and it continues to run, even if the app is no longer running, until there are no pending requests to make, at which point it stops.
package com.yourproject;
import android.app.Application;
import com.dridian.android_rest_request.PendingWebServiceQueueApplicationHelper;
public class MyApplication extends Application
{
public void onCreate()
{
super.onCreate();
/**
* This next line starts the background
* service that runs queued HTTP request
* which failed previously
*/
PendingWebServiceQueueApplicationHelper.getInstance(this);
}
}
package com.dridian.android_rest_request_test;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootBroadcastReceiver extends BroadcastReceiver
{
public void onReceive(Context context, Intent intent)
{
/**
* Do nothing, handled in Application subclass
* because the onCreate() method will run.
* Just be sure that your Application subclass has
* something like this in the onCreate() method:
* PendingWebServiceQueueApplicationHelper.getInstance(this);
*/
}
}
And don't forget to
register your BroadcastReceiver
in the manifest. Add this in the application
node (change the package name accordingly):
<receiver android:name="com.dridian.android_rest_request_test.BootBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
And, of course, you'll need permission to receive boot notifications, so add this in the manifest's application
node:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />