Today we’d like to take a quick tour of one of the lesser known, but highly useful types of services you might want to use: the IntentService.
IntentService (android.app.IntentService) is a simple type of service that can be used to handle asynchronous work off the main thread by way of Intent requests. Each intent is added to the IntentService’s queue and handled sequentially.
IntentService is one of the simplest ways to offload “chunks” of processing off the UI thread of your application and into a remote work queue. There’s no need to launch an AsyncTask and manage it each and every time you have more processing. Instead, you simply define your own service, package up an intent with the appropriate data you want to send for processing, and start the service. You can send data back to the application by simply broadcasting the result as an Intent object, and using a broadcast receiver to catch the result and use it within the app.
Step 0: Getting Started
We have provided a sample application which illustrates the difference between trying to perform processing on the main UI thread (a no-no for application responsiveness) and offloading that same processing to an IntentService. The code can be downloaded via the code download link at the top of this tutorial.
Step 1: Defining the Processing of Messages
First, it’s important to understand when and why to use services in general. One good reason to use an IntentService is when you have work that needs to occur off the main thread to keep the application responsive and efficient. Another reason is when you may have multiple processing requests, and they need to be queued up and handled on the fly.
So let’s say we have an app that needs to do some "processing." We wanted something simple, so we basically will define processing in this case as taking in a string parameter, doing "stuff" to it, and returning the string result. To keep this tutorial simple, the "stuff" we will do is sleep for 30 seconds, pretending to do something useful. In reality, the "stuff" would likely be image processing, connecting to a network, or some other blocking operation.
Therefore, if the entire "processing" were to occur within the main application, you might have an EditText control for taking message input, and a TextView control for splitting out the result. You could place this code in a Button handler to trigger the processing. The code in the Button click handler within the app Activity class would look something like this:
EditText input = (EditText) findViewById(R.id.txt_input); String strInputMsg = input.getText().toString(); SystemClock.sleep(30000); // 30 seconds, pretend to do work TextView result = (TextView) findViewById(R.id.txt_result); result.setText(strInputMsg + " " + DateFormat.format("MM/dd/yy h:mmaa", System.currentTimeMillis()));
The results are not ideal in any way. As soon as the user clicks the button, the entire application becomes unresponsive. The screen is frozen, the user cannot continue with their business, add any new messages. The user basically has to wait around for the processing to finish before they can do a thing.
Step 2: Implementing an IntentService
We would much rather have our processing not interfere with the application. We would also like to be able to add multiple message process requests easily. This is the perfect time to use an IntentService! So let’s implement one.
Create another class file in your project and add stubs for the methods you need to implement. You should add a constructor with the name of your new service. You will need to implement just one other method called onHandleIntent(). This method is where your processing occurs. Any data necessary for each processing request can be packaged in the intent extras, like so (imports, comments, exception handling removed for code clarity, see the full source code for details):
public class SimpleIntentService extends IntentService { public static final String PARAM_IN_MSG = "imsg"; public static final String PARAM_OUT_MSG = "omsg"; public SimpleIntentService() { super("SimpleIntentService"); } @Override protected void onHandleIntent(Intent intent) { String msg = intent.getStringExtra(PARAM_IN_MSG); SystemClock.sleep(30000); // 30 seconds String resultTxt = msg + " " + DateFormat.format("MM/dd/yy h:mmaa", System.currentTimeMillis()); } }
Note we also define the intent extra parameters for the incoming and outgoing intent data. We use the PARAM_IN_MSG extra for the incoming message data. We will soon use the PARAM_OUT_MSG extra to send results back to the main application.
Step 3: Launch the Service from your Application Activity
Next, you need to start the service from your application activity. Add a second Button control that, instead of doing the processing on the main thread, delegates the processing to your new service instead. The new button handler looks like this:
EditText input = (EditText) findViewById(R.id.txt_input); String strInputMsg = input.getText().toString(); Intent msgIntent = new Intent(this, SimpleIntentService.class); msgIntent.putExtra(SimpleIntentService.PARAM_IN_MSG, strInputMsg); startService(msgIntent);
The code for the service method of processing is pretty straightforward. First, an Intent instance is generated and any data (like the message text) is packaged in the intent using the extras. Finally, the IntentService is started with a call to startService().
The service takes over from here, catching each intent request, processing it, and shutting itself down when it’s all done. The main user interface remains responsive throughout the processing, allowing the user to continue to interact with the application. The user can input multiple messages, hit the button again and again, and each request is added to the service’s work queue and handled. All in all, a better solution.
But we’re not quite done yet.
Step 4: Define the Broadcast Receiver
Your IntentService can now do its job of processing, but we need it to inform the main application’s activity when it’s processed each request so that the UI can be updated. This only really matters when the activity is running, so we’ll use a simple broadcast sender/receiver model.
Let’s begin by defining a BroadcastReceiver subclass within the main activity.
public class ResponseReceiver extends BroadcastReceiver { public static final String ACTION_RESP = "com.mamlambo.intent.action.MESSAGE_PROCESSED"; @Override public void onReceive(Context context, Intent intent) { TextView result = (TextView) findViewById(R.id.txt_result); String text = intent.getStringExtra(SimpleIntentService.PARAM_OUT_MSG); result.setText(text); } }
The onReceive() method does all the work. It updates the activity’s TextView control based upon the intent extra data packaged in the incoming result.
Note: If you were updating a database or shared preferences, there are alternate ways for the user interface to receive these changes. In these cases, the overhead of a broadcast receiver would not be necessary.
Step 5: Broadcast the Result
Next, you need to send a broadcast from the onHandleIntent() method of the IntentService class after the processing is complete and a result is available, like this:
// processing done here…. Intent broadcastIntent = new Intent(); broadcastIntent.setAction(ResponseReceiver.ACTION_RESP); broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT); broadcastIntent.putExtra(PARAM_OUT_MSG, resultTxt); sendBroadcast(broadcastIntent);
To send the result back to the main application, we package up another intent, stick the result data in as an extra, and blast it back using the sendBroadcast() method.
Step 7: Register the Broadcast Receiver
Finally, you must instantiate and register the receiver you’ve defined in your activity. Register the receiver in the onCreate() method with the appropriate intent filter to catch the specific result intent being sent from the IntentService.
public class IntentServiceBasicsActivity extends Activity { private ResponseReceiver receiver; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); IntentFilter filter = new IntentFilter(ResponseReceiver.ACTION_RESP); filter.addCategory(Intent.CATEGORY_DEFAULT); receiver = new ResponseReceiver(); registerReceiver(receiver, filter); } }
The result: each time the IntentService finishes processing a request, it fires off a broadcast with the result. The main activity can listen for the broadcast, and the onReceive() method of the broadcast receiver does the rest updating the UI accordingly. Don’t forget to unregister the receiver in the proper time as well (onPause() is recommended).
Conclusion
Offloading work from the main UI thread of an application to a work queue in an IntentService is an easy and efficient way to process multiple requests. Doing so keeps your main application responsive and your users happy. For many purposes, using an IntentService may be easier and more desirable than an AsyncTask, which is limited to a single execution, or a Thread, which has more coding overhead.
About the Authors
Mobile developers Lauren Darcey and Shane Conder have coauthored several books on Android development: an in-depth programming book entitled Android Wireless Application Development, Second Edition and Sams Teach Yourself Android Application Development in 24 Hours, Second Edition. When not writing, they spend their time developing mobile software at their company and providing consulting services. They can be reached at via email to [email protected], via their blog at androidbook.blogspot.com, and on Twitter @androidwireless.
Need More Help Writing Android Apps? Check out our Latest Books and Resources!
There's also a huge selection of Android app templates available on Envato Market to help you get a head-start with your Android development projects.
Comments