Thursday, March 31, 2011

Android Thread Constructs(Part 4): Comparisons

In this series of posts we have seen the following thread constructs:
1. Basic threads and communication between them [see article]
2. Understanding the Main thread or the UI thread [see article]
3. IntentService [see article]
4. AsyncTask [see article]

NOTE:  These are Android specific constructs. Android also includes the java.util.concurrent package which can be leveraged for concurrent tasks. That is out of the scope of my discussion.]

So, when to use what ? What is the exact purpose of each ? What are the drawbacks of using one over the other ? Which one is easier to program ?

I came across all these questions when trying to understand each of these. In due course of time, I have come up with some guidelines which I discuss in this post.

NOTE: Any of the recommendations made in this post and the table below are not comprehensive and final. There may be better and alternate ways of doing things. I would love to know any different views and get an insight into a different thought process. The views expressed in this post are my personal views.

Service
Before proceeding further, let me touch on the Service class. Traditionally speaking, the notion of service reminds us of task running in the background while we can work and interact with the UI. This causes confusion for newbies. Because in Android, even if the Service runs in the background, it runs on the Main Thread of the application. So, if at the same time if you have an activity displayed, the running service will take the main thread and the activity will seem slow. It is important to note that a Service is just a way of telling Android that something needs to run without a user interface in the background while the user may not interacting with your application. So, if you expect the user to be interacting with the application while the service is running and you have a long task to perform in a service, you need to create a worker thread in the Service to carry out the task.
So, even if Service is not a threading construct, its a way of executing a task at hand. Hence I have included it in the comparison.

The table below tries to summarize various aspects of four task execution mechanisms : Service, Thread, IntentService, AsyncTask.

Most of the points in the table are self-explanatory. However, some points that need an explanation are numbered and explained  below the table. Also, this table is just to summarize about the concepts discussed in the previous posts. So, if anything is still unclear, I recommend to go through each individual posts in this series.



ServiceThreadIntentServiceAsyncTask
When to use ?Task with no UI, but shouldn't be too long. Use threads within service for long tasks.- Long task in general.

- For tasks in parallel use Multiple threads (traditional mechanisms)
- Long task usually with no communication to main thread.
(Update)- If communication is required, can use main thread handler or broadcast intents[3]

- When callbacks are needed (Intent triggered tasks). 
- Relatively long task (UI thread blocking) with a need to communicate with main thread.[3]

- For tasks in parallel use multiple instances OR Executor [1]
TriggerCall to method
onStartService()
Thread start() methodIntentCall to method execute()
Triggered From (thread)Any threadAny ThreadMain Thread (Intent is received on main thread and then worker thread is spawed)Main Thread
Runs On (thread)Main ThreadIts own threadSeparate worker threadWorker thread. However, Main thread methods may be invoked in between to publish progress.
Limitations /
Drawbacks
May block main thread- Manual thread management

- Code may become difficult to read
- Cannot run tasks in parallel.

- Multiple intents are queued on the same worker thread.
- one instance can only be executed once (hence cannot run in a loop) [2]

- Must be created and executed from the Main thread

[1] API Level 11 (Android 3.0) introduces the executeOnExecutor() method, that runs multiple tasks on a thread pool managed by AsyncTask. Below API Level 11, we need to create multiple instances of AsyncTask and call execute() on them in order to start parallel execution of multiple tasks.

[2] Once you create an object of an AsyncTask and call execute, you cannot call execute on that object again. Hence, trying to run an AsyncTask inside a loop will require you to each time create a new object in the loop before calling the execute on it.

[3] Recently I was brought to notice that AsyncTask is not recommended for long running tasks. While this is technically possible, I agree with the commenter. The idea here is any task which would potentially block the UI thread - I referred to this as a long task. May be I should have been clear. Thank you for pointing it out.


To be very precise, you cannot do something like :
        TestAsyncTask myATask = new TestAsyncTask();
        for (int i = 0; i < count; i++) {
            myATask.execute("one", "two", "three", "four");
        }

But you can do :
        for (int i = 0; i < count; i++) {
            TestAsyncTask myATask = new TestAsyncTask();
            myATask.execute("one", "two", "three", "four");
        }

[3] Thanks to comment posted by Mark Murphy (@commonsguy) : "there's nothing stopping an IntentService from communicating back to activities. A broadcast Intent is good for that, particularly with setPackage() to keep it constrained to your app (available on Android 2.1, IIRC). An ordered broadcast can be particularly useful, as you can rig it up such that if none of your activities are on-screen, that some manifest-registered BroadcastReceiver picks up the broadcast and raises a Notification."

Conclusion:
With a pretty understanding of the AsyncTask and the IntentService classes provided by Android, it would be a good idea to leverage these for most of the tasks as against trying to manage threads manually or implement your class as a Service unless there is really a need to do so.

Tricky Things:
There are some tricky scenarios that need some special handling. After all, we are dealing with a mobile environment and things are pretty unpredictable. For instance, the user opens your activity in which you start an AsyncTask, but before completion of the task, the user decides to exit the application (say by pressing the back button). Or your activity may be terminated (read kicked off) due to say an incoming call, when you are running a long task in the background. In such cases, where your application is not shutdown, but any foreground tasks have been closed or changed, all the background tasks need to know of this and try to exit gracefully.

19 comments:

  1. FYI, there's nothing stopping an IntentService from communicating back to activities. A broadcast Intent is good for that, particularly with setPackage() to keep it constrained to your app (available on Android 2.1, IIRC). An ordered broadcast can be particularly useful, as you can rig it up such that if none of your activities are on-screen, that some manifest-registered BroadcastReceiver picks up the broadcast and raises a Notification.

    ReplyDelete
  2. Thanks for the input Mark. I'll update accordingly. I like the idea of broadcast intents with the setPackage().

    ReplyDelete
  3. Hi Tejas,
    Thanks for this great article. You covered a lot of what I already knew but shed some light on something that I was tossing up about.

    What would you think is the best solution for this :
    - I want to run multiple tasks asynchronously i.e spawning multiple threads or AsyncTasks from a for loop, but I want to know when the last thread/task has finished before I update the UI.

    Should I keep a type of thread/task count?

    Regards,

    Steve.

    ReplyDelete
  4. Hi Steve,

    Thanks for the comment. you can use the postExecute to update some static variable. When the count reaches the number of tasks you had spawed, you can be sure that all of them have finished.

    Hope this helps.
    Tejas (@advantej)

    ReplyDelete
  5. @tegas
    Throughly enjoyed working through the series of four posts. Rare to see diagrams. Brill!

    If an IntentService can communicate back to the UI thread, then there doesn't seem to be much of a difference in capability between AsyncTask and IntentService? Is that correct? If so, programming with IntentService appears to be simpler than AsyncTask?

    ReplyDelete
  6. @spider Thanks for the encouraging comment !

    There are different ways you can invoke IntentService and AsyncTask. IntentService can be used as a callback since it is triggered using an Intent. AsyncTask on the other hand has to be invoked from the code.
    You can pass multiple parameters to AsyncTask, which you can achieve in IntentService as well.

    The point to remember is that both are convenient classes and depending on the design of the program can be used.

    Yes, I prefer IntentService because of the simpler usage.

    ReplyDelete
  7. @tejas
    What kind of programs would require a design decision to use IntentService over AsyncTask and vice-versa? Using a Twitter-like app as the example, the consumer simply i) writes and then sends the message by pressing the Submit button and ii) reads (in a scrollable window displaying N) messages by pressing the Read button and Swipe up/down.

    The actions for i) and ii) are both determined by UI interaction, and require communication across a network to another program and a database. Both actions could be defined by IntentService or AsyncTask. What would the program design decision thinking be over which one to implement?

    ReplyDelete
  8. @spider As I said both are convenience classes and can be made perform each others tasks. However, there are certain hooks into the AsyncTask e.g. reporting back the status to the main thread - this is built into the class. You can pass and get back multiple parameters. Also API 11 onwards, you can use AsyncTask to run multiple tasks in parallel.

    On the other hand IntentService is light weight and it is not designed to communicate back, but as you see we can by sending a broadcast intent (which may be sniffed).

    For your example, I would prefer IntentService for i) and may be AsyncTask for ii) since you may want to start the download of tweets and display them as and when those are available.

    As a part of design, I would think of how each class Best fits the problem at hand.

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
  10. @indicator veritatis thank you for the comment. Infact I do have an article which talks about handler. I have linked it above at the start. (basic theads and communication between them ) Is there something missing in that one. Let me know, I'll correct accordingly.

    ReplyDelete
  11. This all looks very good, but you mention 'handler' without covering 'Handler'. Since before AsyncTask, the classes Looper and Handler WERE the main classes meant for handling multi-tasking in Android, the omission is glaring. Especially since the reader cannot really understand what "main loop handler" means without understanding what a Handler is: 'handler' is an instance of 'Handler'.

    Now of course, this is a problem mainly for people who did not read the first article in your serious. And since my first reply, I have seen it. Please see my comment on that one.

    Finally, in your columns in this article comparing 'Threads' with 'IntentService' and 'AsyncTask', Handler/Looper is conspicuously absent. Did you mean for the reader tounderstand 'Threads' as 'Threads with Looper/Handler'? If so, you might want to state that more explicitly here, since as pointed out there, 'Threads' and 'Looper/Handler/MessageQueues' really are distinct concepts.

    ReplyDelete
  12. @indicator I appreciate your comments. I'll go through tthem in detail and make changes in order to avoid any confusion. I agan thank you for your suggestions.this will only help me improve my ideas as well as my blog.

    ReplyDelete
  13. AsyncTask's execute method must be invoked on the UI thread

    ReplyDelete
  14. Hi Tejas,

    What approach will you recommend for following scenario:

    Activity contains MapView with nearby friends markers. Friends location should be periodically updated according to current map bounds.

    Currently I use ScheduledExecutor for this.

    ReplyDelete
  15. @Капитан Assuming, you need to update the location(s) only when the activity is being displayed, you can use the AsyncTask. In the doInBackground process you may get the latest location (from whatever your source is) and then call the publishProgress periodically.

    ReplyDelete
  16. May you please expand on your last section: Tricky Things. I can not find much documentation on running an Async Task for the life of an activity, but I run into issues when I need to pause, phone rings, or even the view changes from portrait to landscape. Any advice would be helpful.

    Felice
    P.S. Great posts

    ReplyDelete
  17. Can you talk about the difference between thread and service in terms of memory usage?

    ReplyDelete
  18. Great posts!.
    I have the same problem as felice say. When the view changes from portrait to landscape, I need to restart threads. Sometimes this will be ineffective.So I have to use Service+thread.

    ReplyDelete