Thursday, April 14, 2011

Android and a GenericizedRequest structure

One thing that I've noticed as of late as that there Android does have a great capacity for asynchronous processing via adapters to java.lang.Thread. However, I've found that when developing domain libraries it's best to hide as much of the synchronicity from the consuming developer to
a) standardized
b) simply

By providing a synchronous and asynchronous call you provide flexibility in how the API is consumed. AND you can guarantee to lighten the load of development by the integration developer. I'll create a standardized API that exposes to execute methods that derived off of a generic class which encapsulates the AsyncTask structure for say http calls if that is what you're wanting to hide. By doing this the API becomes very light, consistent and reusable across applications.

Callback is the key for onPost AsyncTask functionality. Will allow the dev to encapsulate as a reusable anonymous inner class.
public abstract class Callback {
 
 Object caller;
 
 Context context;
 
 public Callback(Object sender, Context context) {
  this.caller = sender;
  this.context = context;
 }
 
 public Object getSender() {
  return caller;
 }
 
 public final void handle(T response, Object... params) {
  doCallback(this.caller, response, params);
 }

 /**
  * The callback template
  * 
  * @param sender
  *            the activity that sent the request
  * @param response
  *            the response of T
  * @param params
  *            the input stack
  */
 protected abstract void doCallback(Object sender, T response, Object... params);
}



GenericRequest:
public abstract class GenericRequest {

 private static final String TAG = "GenericRequest";

 protected HttpRequest requestor;

 protected String url;

 public GenericRequest() {

 }

 public GenericRequest(HttpRequest requestor, String url) {
  this.requestor = requestor;
  this.url = url;
 }

 /**
  * Will make the request in a synchronous fashion
  * 
  * @return
  * @throws Exception
  */
 public T execute() throws Exception {
  assert url != null;
  assert requestor != null;
  requestor.makeRequest(url);
  String offTheWire = requestor.getStringResponse();
  return parseResponse(offTheWire);
 }

 /**
  * Will invoke an request as an AsyncTask on a thread and the
  * {@link Callback} will be invoked at the end of the execution cycle
  * 
  * @param callback
  *            the callback functionality to be made
  * @throws Exception
  *             any exception
  */
 public void execute(Callback callback) throws Exception {
  Log.d(TAG, "executing(..)");
  if (callback != null) {
   CallingAsyncTask caller = new CallingAsyncTask(callback);

   caller.execute(url);
  }
 }

 protected abstract T parseResponse(String httpResponse);

 /**
  * An inner class to handle AsyncTasks for {@link HttpRequest} if a
  * {@link Callback} method is passed to an
  * {@link GenericRequest#execute(Callback)}
  * 
  * @author Daniel Williams
  * 
  */
 class CallingAsyncTask extends AsyncTask {

  static final String TAG = "CallingAsyncTask";

  Callback callback;

  String url;

  public CallingAsyncTask(Callback callback) {
   this.callback = callback;
  }

  /**
   * Will execute a single URL and invoke the parent's
   * {@link GenericRequest#parseResponse(String)} method.
   * 

* This method by default will return java.lang.Void as we are * leveraging the Callback to handle the return to the caller. */ @Override protected T doInBackground(String... params) { Log.d(TAG, "doInBackground"); T response = null; if (params == null || params.length > 1 || params.length == 0) throw new RuntimeException("Must handle a single URL."); else this.url = params[0]; try { Log.d(TAG, "Requesting " + params[0]); requestor.makeRequest(new HttpGet(params[0])); Log.d(TAG, "request completed."); } catch (Exception e) { Log.d(TAG, e.getMessage()); throw new RuntimeException(e.getMessage(), e); } response = parseResponse(requestor.getStringResponse()); Log.d(TAG, "doInBackground complete"); return response; } @Override protected void onPostExecute(T result) { Log.d(TAG, "onPostExecute"); callback.handle(result, url); } } }



And the API Request class:
public class Request extends GenericRequest {

 public Request() {
 }
 
 /**
  * Will by default call the parent
  * @param requestor
  * @param url
  */
 public Request(HttpRequest requestor, String url) {
  super(requestor, url);
 }

 /* (non-Javadoc)
  * @see com.nbc.cnbc.android.GenericRequest#parseResponse(java.lang.String)
  */
 @Override
 protected Response parseResponse(String httpResponse) {
      // parse your httpResponse
 }
}


By solidifying this pattern via templates, anonymous inner classes for Callbacks and encapsulated AsyncTasks your API can be easily executed as:

// synchronously
Request r = new Request(new HttpRequest(), "http://www.google.com");
Response response = r.execute();

// or Asynchronously
Request r = new Request(new HttpRequest(), "http://www.google.com");
Response response = r.execute(new Callback(this, this)( {
    @Override
    public void doCallback(Object sender, Response response, Object... params) ({
         this.context.......
         this.sender.notify();
      });

No comments: