Sunday, January 5, 2014

Android's IntentService

When I ask my colleagues about IntentService as a way to execute asynchronous tasks, most seems to be unaware of it existence. Perhaps it is because it doesn't interact with the UI/main thread like AsyncTask does, so it's use is limited to background processes that doesn't require UI feedback, and so there are less scenarios where it is appropriate. IntentService is just one of many ways Android provide asynchronous operations. Others include
  • runOnUIThread
  • Executor
  • Handler
  • AsyncTask
  • Service
  • Loader

Service vs IntentService


The first question that most developer ask is what's the difference between a normal Android Service and an IntentService. Most folks understand that a Service is an application component that can perform long running tasks in the background without UI feedback. If you read the Class Overview of IntentService, you would read that
"IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand."
The first time I read this quote, I interpreted to mean that Service extends IntentService. But the opposite is true.
public abstract class IntentService extends Service
IntentService does a little bit more than what Service does for you.
  • While a regular Service runs in the caller's thread, which in most cases is the main thread, IntentService always run on a background thread.
  • IntentService uses a message queue to process requests, so only one intent is handled at any time. 
    • if you need parallel tasks such as downloading multiple images at the same time, IntentService may not be a good choice. 
    • If one of the tasks is stuck, then all the pending tasks in the queue will not execute. 
  • Calling stopService is optional; The IntentService will stop itself once there's no more work on the queue. 
  • A broadcast receiver is needed if you want to provide UI feedback.

Implementing an IntentSevice


There are only a few things you have to do:
  1. Declare a few constants for communication between application and IntentService.
  2. Create a class that extends IntentService; Implements onHandleIntent method.
  3. Create a BroadcastReciever; Implement onReceive method.
  4. Trigger the IntentService by calling startService, with input arguments wrapped in an Intent.

Example:



Handling device rotation


In order to handle configuration change properly, when the device is rotated for example, the broadcast receiver should be bind and unbinded along with the lifecycle of the Activity or Fragment


Sample output


Note that the requests are handled serially such that if startService was called by mistake more than once with the same Intent, the work will be done twice, so it's important to either prevent that from happening in the calling side or write the IntentService such that it recognize a duplicate request. This can be accomplished by a runtime cache or input and output. So if the service receive the Intent and it already cached the result, then just broadcast the result again. Finally, if you call stopService all the pending tasks will be lost, which maybe what you want.


Calling startService just once


12-13 15:44:11.300 19913-19913/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:44:11.370 19913-19913/com.mobitv.demo I/System.out﹕ MyBackgroundService.onCreate
12-13 15:44:11.380 19913-20063/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Find price for GOOG
12-13 15:44:13.242 19913-20063/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Found price: $1200.45
12-13 15:44:13.252 19913-19913/com.mobitv.demo I/System.out﹕ MyBkgSvcResponseReciever.onRecieve
12-13 15:44:13.272 19913-19913/com.mobitv.demo I/System.out﹕ MyBackgroundService.onDestory


Calling startService many times in rapid succession.


12-13 15:46:28.568 20388-20388/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:46:28.628 20388-20388/com.mobitv.demo I/System.out﹕ MyBackgroundService.onCreate
12-13 15:46:28.638 20388-20494/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Find price for GOOG
12-13 15:46:28.688 20388-20388/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:46:28.838 20388-20388/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:46:28.989 20388-20388/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:46:29.129 20388-20388/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:46:30.520 20388-20494/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Found price: $1200.45
12-13 15:46:30.530 20388-20494/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Find price for GOOG
12-13 15:46:30.550 20388-20388/com.mobitv.demo I/System.out﹕ MyBkgSvcResponseReciever.onRecieve
12-13 15:46:32.422 20388-20494/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Found price: $1200.45
12-13 15:46:32.422 20388-20388/com.mobitv.demo I/System.out﹕ MyBkgSvcResponseReciever.onRecieve
12-13 15:46:32.442 20388-20494/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Find price for GOOG
12-13 15:46:34.324 20388-20494/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Found price: $1200.45
12-13 15:46:34.324 20388-20388/com.mobitv.demo I/System.out﹕ MyBkgSvcResponseReciever.onRecieve
12-13 15:46:34.354 20388-20494/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Find price for GOOG
12-13 15:46:36.246 20388-20494/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Found price: $1200.45
12-13 15:46:36.256 20388-20388/com.mobitv.demo I/System.out﹕ MyBkgSvcResponseReciever.onRecieve
12-13 15:46:36.286 20388-20494/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Find price for GOOG
12-13 15:46:38.168 20388-20494/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Found price: $1200.45
12-13 15:46:38.168 20388-20388/com.mobitv.demo I/System.out﹕ MyBkgSvcResponseReciever.onRecieve
12-13 15:46:38.248 20388-20388/com.mobitv.demo I/System.out﹕ MyBackgroundService.onDestory


Calling startService many times in rapid succession, then calling stopService once.


12-13 15:47:19.849 20807-20807/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:47:19.909 20807-20807/com.mobitv.demo I/System.out﹕ MyBackgroundService.onCreate
12-13 15:47:19.919 20807-20952/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Find price for GOOG
12-13 15:47:20.009 20807-20807/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:47:20.209 20807-20807/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:47:20.409 20807-20807/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:47:20.590 20807-20807/com.mobitv.demo I/System.out﹕ Button.setOnClickListener calls startService
12-13 15:47:20.940 20807-20807/com.mobitv.demo I/System.out﹕ MyBackgroundService.onDestory
12-13 15:47:21.791 20807-20952/com.mobitv.demo I/System.out﹕ MyBackgroundService.onHandleIntent; Found price: $1200.45
12-13 15:47:21.801 20807-20807/com.mobitv.demo I/System.out﹕ MyBkgSvcResponseReciever.onRecieve



Example Use in projects

To see how others have used IntentService, I went on Github and did a search and found a few examples of open source project that uses IntentService as a background task.
  1. RingerMuteService uses IntentService to change the volume level of the device
  2. VBox's EventIntentService polls VirtualBox for events and publish them in a local broadcaster
  3. WidgetCreatorService uses IntentService to create app shortcut on the device home page
  4. WotdWidget updates data in an Android widget.
  5. UploadToJBossServerService upload file to a JBoss Server.

1 comment: