by Andy Gibel
The following post covers the theoretical formulation and implementation of an Android IntentService that provides for the centralization and delegation of network calls that an application might make while communicating with a web service.
There are many forums and blogs that discuss the relative merits and uses of different tools such as the AsyncTask, Java Thread, Looper, Handler, Loader, and the list goes on. I propose that the following method produces one of the most scalable, modular, and elegant solutions possible given this particular problem space.
Stepping back for a moment, I’d like a chance to explain why this solution is worth blogging about at all.
If one’s world view were completely based on the field of Computer Science, they would likely be convinced that everything happens in a controlled and sequential manner. It’s a model that was created by Turing and others in an attempt to simplify the inherently complex and concurrent world we live in.
Concurrent programming has long been seen as an advanced topic or even as niche as in the field of supercomputing where machines try to make the most out of every cycle of an expensive CPU. Mobile devices change this landscape drastically as they require an increasing number of simultaneous tasks to be executed often taking advantage of multiple physical CPU cores.
Even in the context of a single process, care must be taken to delegate blocking or long running code to separate threads of execution. In the world of Android, not adhering to this rule will quickly land you in the painful world of the Application Not Responding (ANR) errors. The problem for Android neophytes is two fold. First, as mentioned, it is entirely likely that newer developers aren’t particularly comfortable with multi-threading to begin with. This is compounded by the second problem which is that the Android framework provides many useful tools to choose from. So many, in fact, that the difference and use cases for each is often a hot topic of debate and confusion.
What is the IntentService and why do I care?
As we know the Android Framework consists of four main components: the Activity, Service, ContentProvider, and Broadcast Receiver. While an Activity has a direct relationship to a visible screen in an application and is meant to be relatively short-lived, the Service has no direct affiliation with an Activity or screen. When there is a need for longer running background processes in an application, the Service is generally the weapon of choice because it can persist across Activity boundaries.
While the Service class can be subclassed directly, it is much more common to use the IntentService flavor due to several inherent benefits:
It launches its own work thread
An important fact that is often overlooked when using a Service is that they do not, by default, create a new thread. Therefore, if a regular Service is subclassed, all of its work will happen on the UI thread unless the implementor explicitly creates another. While this is perhaps a semantic difference, the IntentService handles this concern by automagically running all of its main loop work on a separate thread - correctly freeing up the UI thread and saving us from writing a fair amount of boiler plate code.
It terminates when the work in its main loop is finished
Services will persist from their creation until they are explicitly stopped. This can be error prone and can produce behaviors that are difficult to catch and debug. The IntentService relieves the caller from the responsibility by simply running the work it is given, and stopping itself.
Ease of implementation when using the Command pattern
A Service is generally started from an Activity. The communication channel between the caller Activity and Service is defined at creation and can use one of two patterns: Command or Bind.
The Command pattern is very simple to implement and understand. When a Service is started, an identifier is passed in along with an Intent. These respectively tell the Service what it should do, and pass the Service the data it needs to do that task. The Identifier is also later used by the calling Activity to determine what the Service was doing when it sends back the result.
To Bind with a Service is to set up a two-way API that can be used by the Activity and Service to communicate for the entire lifecycle. While this gives more control over how the two send messages, it is significantly more difficult to implement well especially when the bound Activity undergoes configuration changes. Since the IntentService simply runs, does its work, and returns a result, it makes it the perfect candidate for the Command pattern.
The code
For the purposes of this post, we will assume we only have three source files: The LoginActivity, the ActionService, and the SuccessActivity. We will simply display two Text fields and a button to a user so that they can log in. If the login is successful, the SuccessActivity comes up and signals that we have successfully logged in. If unsuccessful, we tell the user to get their act together and try again.
To let our Service have more than one possible action to take, we also provide a button called “Info” on the login screen that will ask the server for the newest info text. On Receipt, our Activity will display the info text in a label below all the other controls.
Architect your Service
All web calls and communications are now encapsulated in our subclass of IntentService. We simply check the incoming message from the calling Activity, act on the request, and return the result.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
|
Call your Service and handle the return
Our main Activity is the LoginActivity. This is simply a screen presented to the user that collects both a username and password input. This information is packaged up and sent to our IntentService so that it may contact our web service for us. We make no direct networking calls in this class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
|
Summary and future work
While this setup may look complicated at first, I found it to be by far the cleanest of my reusable patterns. This example includes only two web calls but one can easily see that it scales very well - simply add additional cases and Action types for the Service to handle. I have used the PendingIntent callback in both these calls which serves us well when we are sitting at a particular Activity and wanting the result. If, however, there will be multiple Activites and you want the result no matter which one the user is on, you might consider Broadcast Receivers. This along with implementation of JSON marshalling/unmarshalling are among the candidates for future blog posts. Meanwhile, I hope to never catch any of you using messy, inline, anonymous AsyncTasks for web calls again!