Anda di halaman 1dari 8

Building a REST Android Client Application

Craig Isakson Software Engineer

marketing + technology 701.235.5525 | 888.9.sundog | fax: 701.235.8941 2000 44th st s | oor 6 | fargo, nd 58103 www.sundog.net

Building a REST Android Client Application | Craig Isakson

As the number of smartphone and Android devices rise, applications that consume RESTful web services also rise. There are a number of different ways a REST Android Application can be built and this white paper will present architectural considerations for developing RESTful applications on the Android platform. It will focus on design patterns, platform integration and performance issues specic to the Android platform.

What is REST?
REST stands for Representational State Transfer. REST is a broadly adopted architecture style that consists of clients and servers. Clients make requests to the server to get or change the particular state of a resource. The server will then respond to the client with the representation of the resource. To fully understand this, it is important to understand what a resource is. A resource can be described as a particular objects properties. For example, if the object is a user, the resource would contain all the pertinent information about the user: name, address, title, etc., and often include an ID for reference to that particular user. The REST API can describe the resource to the user in a number of different ways including XML, JSON and Binary. Typically, it is human nature to take the path of least resistance. Therefore, when building a REST Android Client Application it may make sense to run the entire application inside of the main activity. Since the REST callout can take some time to complete, the main activity would simply spin off a new thread for the callout, store the response from the server in memory, and update the information within the activity when it is nished. Building the application this way would produce an application that was fast and would work most of the time. Why is building an application this way bad? In order to answer this, an understanding of mobile and the Android activity/service lifecycle is a must. Unlike a typical desktop computer, a mobile phone has limited resources. These resources are not just limited by processing power and memory, but also limited network connectivity and power. With the previous example of the REST application, the application would make a callout each time the data needed to be displayed. Since the data is dependent on network access, the application being able to display data to the user is not guaranteed. Another downside to this is that extra callouts consume more data as well as more battery. Another issue to consider when dealing with mobile is that a connection to the network is not always guaranteed. An activity has its own lifecycle. An activity has a number of different states including running, resume, pause and destroy. In the previous example of the REST application, if the application is currently making a callout to get or change information on the server and the user receives an incoming phone call, the applications state is switched to a pause mode. All the information that was retrieved from the sever for that callout will be lost and when the user returns to the application, they will need to make another callout. Worse yet, if the user was updating information on the server through a POST or Delete, the user would have no way of knowing if the information was successfully changed or submitted. Another use case that would have issues would be if the application had all the data it needed and was simply paused by the user opening a different application. After the application was paused, the phone would then start to run out of memory. The garbage collector would go out looking for places to free up memory. Activities with no services attached are the rst to get destroyed when trying to free up memory. The garbage collector would destroy the application and when the user re-opens, the application would not be saved, which would require more callouts to the REST service to retrieve the same information the user just had.

Building a REST Android Client Application | Craig Isakson

There are better ways to make REST callouts in Android. The easiest to implement is a design pattern created by Virgil Dobjanschi of Google. In one of his design patterns, the developer of the application would use services, intents, and a database to maintain the state of the data being handled in the application. Below is a diagramed overview depicting the ow of his design.

In an effort to describe the above design pattern, the following example will start at the bottom and work its way up through the different pieces explaining what they are and how they work. Code samples will also be shown where applicable from the Android User Group of MN sample application that they have available on GitHub. The rst piece of the pattern that will be discussed is the RESTMethod. The RESTMethod is an entity which actually prepares the HTTP URL & HTTP request body, executes the HTTP transaction, and processes the HTTP response. Since this method can often take some time, it should always be run in a worker thread. It is also ideal to use the Apache HTTP client build into Android and not the Java URL connection. This method should also re back a Java listener callback. The next piece of the design pattern is the processor. The role of the processor is to mirror the state of the resource from the server on the device. This means that the application will use a database to save the information. The schema of the data in the database should match the resource on the server with the addition of two columns; a status column for describing the state of the resource and a response column used to save the response received from the server for that resource. The reason for the extra columns is to give the activity information on the state of the particular resource. This allows the application to continue to function and not have to focus on what is happening with that resource. At any time, if the application needs to nd out what the current state of that resource is, it could simple query the database for the most up-to-date information. This means for each RESTMethod call there will be two database transactions: one when the call is made and one when the call has nished. Another benet to using these ags is when a transaction was not successful, the data and the state is still there, so it makes it very simple to retry an operation which means no loss of data for the user.

Since this is a service based API, it would help to know what a service is. A service is a facility for the application to tell the operating system about a process it wants to run in the background. There is a lot of confusion around in which thread the service runs. The service runs in the context of the main UI thread, so simply starting a service does not start a new thread. If the service is going to perform long running processes, these processes should be started in a worker thread. This API will also use intents. An intent is an abstract description of an operation to be performed. An intent is used to perform late runtime binding between the code in different applications. It is a passive data structure holding an abstract action that can be performed often between different applications and activities.

Building a REST Android Client Application | Craig Isakson

Just like with the RESTMethods, all database operations should be performed within the context of a worker thread. The next piece of the design pattern is the service. The service is there to allow long running operations to be performed while the activity is long gone. Since intents are being used to start the service, the service itself is stateless. It receives all the information it needs from the intent that was passed to it. On the forward path, the service receives the intent sent by the service helper and starts the corresponding RESTMethod. On the return path, the service handles the processor callback and invokes the Service Helper binder callback. Just like the RESTMethod and the processor, all of the long running operations within the service need to be within the context of a worker thread. It is also important to stop the service whenever it has completed the callback to the service helper. If the service is not stopped, Android will see that the service is up and running and if, or when, other applications need more device resources, they will be unable to receive them because the service is still sitting in memory. The next piece of the design pattern is the service helper. The service helpers job is to provide a simple asynchronous API to the user interface. This makes it easy for the user interface to call the service methods and not focus on the state of the data. The service helper will prepare and send the service request by checking if the method is already pending, creating the request intent, adding the operation type and unique request id, adding the method specic parameters, adding the binder callback, calling the startService(intent), and returning the request id. On the return path, the service helper will handle the callback from the service and dispatch callbacks to the user interface listeners. Once the user interface knows that the request is complete, since the data has already been saved to the database within the processor, the main activity can retrieve the data. The nal piece of the design pattern is the activity. Within the activity, there needs to be an operation listener to essentially determine if the service has been completed. This operation listener would use a request id to determine the state of the resource. Also, this listener would need to be added to the onResume method in the

activity and removed from the onPause method within the activity. There are a few different use cases that must be considered when dealing with the request. The rst is that the activity is still active when the request completes. This is fairly simple and can be handled directly with the listener that was created. The second is that the activity is paused then resumed while the request completes. This is also fairly simple as the listener that was added in the onResume method would handle this use case. The most difcult to handle is when the activity is paused and the request is completed while the activity is paused. The activity will then resume and check to see if the callout was completed. It would discover that the callout was completed and since the response from the RESTMethod was stored in the database for that particular resource, the activity can simply query the database to retrieve the information. Now that each specic portion of the design pattern has been explained, it is now possible to run through a full example from activity to RESTMethod and back. This example will use add a comment to a picture as the sample objective. The activity will make a simple call to the service helper to add the comment with all the data that is needed:

Building a REST Android Client Application | Craig Isakson

mServiceHelper.submitNewComment(this.Id, comment.toString()); From here, the service helper will take the request and package it up into an intent and send it off to the service. The service will return the request id, which can be used later to check the status of the request: public long submitNewComment(String catId, String comment) { long requestId = generateRequestID(); synchronized (pendingRequestsLock) { pendingRequests.add(requestId); } Intent intent = new Intent(this.mAppContext, mServiceClass); intent.putExtra(ServiceContract.METHOD_EXTRA, ServiceContract.METHOD_POST); intent.putExtra(ServiceContract.RESOURCE_TYPE_EXTRA, ServiceContract.RESOURCE_TYPE_COMMENTS); intent.putExtra(ServiceContract.SERVICE_CALLBACK_EXTRA, serviceCallback); intent.putExtra(EXTRA_REQUEST_ID, requestId); Bundle requestParams = new Bundle(); requestParams.putString(CommentsTable.PICTURE_ID, pictureId); requestParams.putString(CommentsTable.COMMENT_TEXT, comment); intent.putExtra(ServiceContract.EXTRA_REQUEST_PARAMETERS, requestParams); this.mAppContext.startService(intent); return requestId; } Now that the service helper has put together the intent and started the service with the intent, the service can process the intent that was sent from the service helper and start the processor to start processing the callout. The service will also check to see what type of operation is being performed (get, put, or delete) to determine what it needs to send back to the service helper: @Override protected void onHandleIntent(Intent requestIntent) { // Get request data from Intent int resourceType = requestIntent.getIntExtra(ServiceContract.RESOURCE_TYPE_EXTRA, -1); String method = requestIntent.getStringExtra(ServiceContract.METHOD_EXTRA); Bundle parameters = requestIntent.getBundleExtra(ServiceContract.EXTRA_REQUEST_PARAMETERS); ResultReceiver serviceHelperCallback = requestIntent.getParcelableExtra(ServiceContract.SERVICE_ CALLBACK_EXTRA); ResourceProcessor processor = ProcessorFactory.getInstance(this).getProcessor(resourceType);

Building a REST Android Client Application | Craig Isakson

if (processor == null){ if(serviceHelperCallback != null){ serviceHelperCallback.send(REQUEST_INVALID, bundleOriginalIntent(requestIntent)); } return; } ResourceProcessorCallback processorCallback = makeProcessorCallback(requestIntent,serviceHelperC allback); if (method.equalsIgnoreCase(METHOD_GET)) { processor.getResource(processorCallback, parameters); } else if (method.equalsIgnoreCase(METHOD_POST)){ processor.postResource(processorCallback, parameters); } else if(serviceHelperCallback != null) { serviceHelperCallback.send(REQUEST_INVALID, bundleOriginalIntent(requestIntent)); } } The processor will now make a call to the RESTMethod to actually make the callout to the server. The processor will then add the data to the database including the state of the resource. The processor will use the callback from the RESTMethod to update the state once again and return the results back to the service: @Override public void getResource(ResourceProcessorCallback callback, Bundle params) { // (1) Call the REST method // Create a RESTMethod class that knows how to assemble the URL, // and performs the HTTP operation. String mPictureId = params.getString(CommentsTable.PICTURE_ID); String apiPicId = getPicRed(mPictureId); RestMethod<Comments> method = new GetCommentsRestMethod(mContext, apiPicId); RestMethodResult<Comments> result = method.execute();

/* * (2) Insert-Update the ContentProvider status, and insert the result * on success Parsing the JSON response (on success) and inserting into * the content provider */ addNewComments(result,mPictureId); // (3) Operation complete callback to Service

callback.send(result.getStatusCode(), null); }

Building a REST Android Client Application | Craig Isakson

The actual code in the processor is a simple REST callout to whichever API is being used. After the result has been passed, the processor will update the content provider, which allows any listeners of the content provider the ability to update their information, such as cursor adapters, as they are received from the REST callback. The processor will let the service know that it is done and what the result is. Then the service will implement the binder callback that it was sent via the intent from the service helper. This will let all the activities know that the call is nished and there is a result. This allows the activity to update data to the user whether it be a success message or some sort of error that may have occurred. While this design pattern is great for most cases, there are a few different use cases where this type of pattern may not be ideal. One such use case would be if the REST callout was being performed in an auto complete text box. In this case, the application would have to make quite a few callouts while the user is typing. There is no need to worry if the callout errors because this is just performing get methods. It would also be excessive to save each result to the database. The design pattern described here functions very well and takes into account a number of different use cases that are often overlooked. Just because an application performs well most of the time, does not mean that the application is following best practices. By using this design pattern, the user will be presented with a much better experience. If all developers applied the same concepts, Android as a whole would benet.

Building a REST Android Client Application | Craig Isakson

References
Google I/O 2010 Android REST client applications. 2010. Available at http://www.youtube.com/ watch?v=xHXn3Kg2IQE. Accessed April 19th, 2012. restful-android. 2012. Available at https://github.com/aug-mn/restful-android. Access April 19th, 2012. public abstract class Service. 2012. Available at http://developer.android.com/reference/android/app/Service.html. Accessed April 19th, 2012. public class Intent. 2012. Available at http://developer.android.com/reference/android/content/Intent.html. Accessed April 19th, 2012.

Anda mungkin juga menyukai