18

Android has a lot of different facilitations of executing code on separate threads concurrently. But I'm not sure when each one should be used or what the best practices are for these different ways.

  1. When/Why should Handlers be used?

  2. When/Why should Loaders be used?

  3. When/Why should AsyncTasks be used?

  4. When/Why should FutureTask be used?

  5. When/Why should Executor be used?

  6. When/Why should Threads/Runnables be used?

  7. When/Why should Volley be used?

Have I missed any?

Thanks very much for the help!

George
  • 235
  • 3
  • 9
  • 2
    This question is a little broad and some could argue different uses for each of your points. Have you read the vogella tutorials? – Graham Smith Sep 05 '13 at 21:36
  • 1
    Hey, I've read all the documentations and various tutorials but it seems that every one of these ways overlaps somewhat onto others. – George Sep 05 '13 at 21:37
  • Yes they do and it makes my point. For example I really do not use asynctasks any more because of issues with rotation and memory leaks. But that doesn't mean someone else might have a good implementation for a different scenario, there is probably a good answer but not a definitive one. – Graham Smith Sep 05 '13 at 21:41
  • I've been curious about this too and I've been doing Android development for awhile. I can give you an answer that covers my experiences though. We'll see if people agree. – dcow Sep 05 '13 at 21:48
  • I guess I was interested in general scenarios where each should be considered a better option then the rest. – George Sep 05 '13 at 21:48
  • 2
    I've been doing android development for some time as well, but this topic still overwhelms and confuses me a bit. – George Sep 05 '13 at 21:49

1 Answers1

24

You have a pretty big list of components each of which address slightly different things. The two main conflicts in my experience are actually between Handlers vs. Executors, and Tasks vs. Threads.

Overview

Threads

Threads are the underlying system mechanism used to facilitate concurrency. Threads exist in Windows, Unix, etc., and may be implemented in any way a system sees fit. It is usually pretty expensive to create threads (and even-more-so to create entirely new processes). I do no know whether the Dalvik VM relies on Linux thread or provides its own Thread implementation. According to this Dalvik Virtual Machine Architecture document, the Dalvik VM simply uses the Linux threading model for its thread implementation. The Dalvik VM does not, however, conform to the Java Language Specification's memory model pre Android 4.0.

As you may know, because it is expensive to create threads, a better way to schedule lots of asynchronous computations is to use a managed pool of threads (size depending on the capabilities of your underlying hardware) and simply feed the tasks to the manager, which, in turn, schedules the tasks on threads as they become available.

Android Handlers

A short overview of the history as I understand it is that Android development began back in the days before the Java Executor libraries/api became solidified. To provide a less confusing concurrency interface than the old Threads and Synchronization methods available, and to facilitate message passing between threads in a nice manner, Android introduced Handlers. However, unless I'm understanding this incorrectly, Handlers are basically the same thing as Executors. You pass Messages (containing Runnables), or simply Runables, to Handlers and these messages get queued and executed as quickly as the Handler's Thread can well.. handle them.

Java Executors

In Android, all communication between threads is (or should be) done via a thread's handler (to my knowledge and excluding situations where you're directly accessing another threads memory and synchronization may be required). In new Java, Executors are functionally equivalent to Handlers, and FutureTasks are equivalent to Messages. Runnables are still Runnables. A Runnable is used when code simply needs to be executed. A FutureTask is used when a computation needs to be executed asynchronously and a result needs to be retrieved from the computation. The result can by any type of object. Because FutureTask implements the Future interface, you may also easily wait in one thread for the Future to resolve and get the result.

Loaders

Loaders are simply an attempt at providing a reusable mechanism for loading data (to be consumed by an activity). Despite the documentation, the Loader class is not abstract nor does it need to be used solely for the asynchronous acquisition of data (although I can't think of a case where you'd use it for synchronous loading, I have not explored that). The AsyncTaskLoader simply uses an AsyncTask to execute the Loader's methods responsible for loading data. Loaders are not necessary at all.

Examples

Threads

Threads should be used to back Executors or Handlers, or, in other cases, you will know when you need a thread (such as to back a Service). You should generally avoid Threads for small tasks that are created, executed, and destroyed in a liberal manner.

Handlers vs Executors

In my experience, it seems better to stick with Handlers when dealing with message passing between your UI thread (running an Activity and/or View hierarchy) and some other thread. However, it seems perfectly safe to use either Executors or Handlers when implementing your own asynchronous functionality. For example, I may have a class that manages a queue of images to be fetched from a server. I can simply create my own executor and feed tasks to that executor as I see fit. But, when passing a message message back to the Activity that one of the images has loaded and the view should be set with the resulting data, I've had best results using the Activity's Handler (accessed via the runOnUiThread() method) as opposed to creating an executor that runs on the Activity's thread and posting Runnables to it. It is unclear why the latter would not work all the time (from the documentation, that is) but I suspect doing so can result in behavior similar to a Handler's runWithScissors() method. (Edit, I'm now aware it is extremely bad practice to run multiple concurrent "loopers" on one thread -- so that explains that.)

I guess a good rule of thumb may be to:

  • Use Handlers when message passing between Android components.
  • Use Executors or Handlers when implementing your own library or utility functionality.

Runnables vs FutureTasks vs AsyncTasks

As stated earlier, Runnables are a simple interface used to pass computations between objects. You need not use Runnables only when threads/executors/handlers are involved -- they can be used to capture state and to reuse code.

  • Runnables and FutureTasks are used with Executors.
  • AsyncTasks are (implicitly) used with Handlers. When you construct an AsyncTask you provide a reference to the activity and the AsyncTask class facilitates the execution of the doInBackground method on one thread from a pool of worker threads (this is internally managed by an Executor). To do this a Handler is used to pass messages between the activity thread and the task's thread. The task itself is managed by an Executor, though, and handed off to a worker thread when one is available.

Loaders

I don't use Loaders. But, they may be used when you have data that you need to load in order for an Activity to use it. You can use a CursorLoader with a ContentProvider if you are following the ContentProvider idiom in terms of handling data.

Volley

I've never used volley so I doubt I have any useful insight on that channel.


Disclaimer: I am not an authority on Android history. Regardless, I hope the overview helps add clarity.

Community
  • 1
  • 1
dcow
  • 7,765
  • 3
  • 45
  • 65
  • 1
    *The Dalvik VM does not, however, conform Java Language Specification's memory model.* => android 4.0+ does: http://stackoverflow.com/a/4610859/829571 – assylias Sep 05 '13 at 23:35
  • Volley is the new api to allow Android to make concurrent request for various types of data...something like an image. But very nice writeup, thanks for the info. I would welcome anyone else to post who has other opinions and propositions. – George Sep 05 '13 at 23:52
  • 1
    This is volley: https://developers.google.com/events/io/sessions/325304728 – George Sep 05 '13 at 23:59
  • @George thanks for the accept but if you want more commentary (as in if I didn't provide information you didn't already know) then feel free to remove the accept. – dcow Sep 06 '13 at 00:33
  • 2
    @dcow ~"Loaders are not necessary at all". Why do you say that? They eliminate a tremendous amount of boilerplate code one would have to write to retain a singleton reference to a database connection, as well as retain cursor state upon configuration changes. – IgorGanapolsky Apr 16 '14 at 21:51
  • @IgorGanapolsky which is why I said they help, but aren't necessary. – dcow Apr 18 '14 at 09:30