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.