Handler vs AsyncTask vs Thread
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
Android gives you several ways to move work off the main thread, but they solve different problems. Thread, Handler, and AsyncTask are often mentioned together even though only one of them was meant to be a complete background-work helper.
What Each Tool Actually Does
A plain Thread is the lowest-level building block in this group. It runs code concurrently, but it has no built-in knowledge of the Android UI thread or the Activity lifecycle.
This works, but you are responsible for cancellation, error handling, and making sure the screen that receives the result still exists.
A Handler is not a background worker by itself. It posts Runnable objects or Message objects onto the message queue of a specific thread that has a Looper. In modern Android code, a common use is posting work back to the main thread.
That makes the role clearer: the Thread does the background work, and the Handler schedules the UI update on the main thread.
AsyncTask tried to combine those ideas into one API: background work in doInBackground, then UI callbacks in onPostExecute. The problem is that this convenience came with real costs. Android deprecated AsyncTask in API level 30 and recommends standard Java concurrency tools or Kotlin concurrency utilities instead.
How to Think About UI Work
The main thread owns most UI objects. That means network calls, database work, image decoding, and other slow operations should not happen there, but final UI updates usually must happen there.
That separation explains why Handler and Thread are often used together:
- '
Threadruns the expensive operation' - '
Handlerposts the result to a thread with aLooper'
If you only start a raw thread and then touch a view directly, you will eventually hit threading bugs. If you only create a Handler on the main thread, you still have not moved the expensive work off the UI thread.
Where AsyncTask Fit Historically
For older Android codebases, you will still see patterns like this:
This made simple one-shot work easy, but it also encouraged code that captured Activity references, leaked views across configuration changes, and behaved differently across Android versions. It was fine for short-lived background work in older apps, but it is not the right default for new development anymore.
If you are maintaining legacy code, understanding AsyncTask still matters. If you are writing new code, it is more useful to understand the underlying concerns: what runs in the background, what returns to the main thread, and who owns cancellation.
Choosing the Right Tool
If you are comparing only these three options:
- Use
Threadwhen you need direct control over a one-off unit of background execution. - Use
Handlerwhen you need to schedule work onto a specific thread, especially the main thread. - Use
AsyncTaskonly when maintaining older code that already depends on it.
In practice, modern Android code usually moves one level higher:
- Use Kotlin coroutines for request-response style asynchronous work
- Use
ExecutororExecutorServicewhen Java-style task execution is a better fit - Use
WorkManagerfor deferrable background work that should survive app restarts
Those tools map better to lifecycle and cancellation concerns than manually stitching together several raw threads.
A Better Modern Equivalent
The closest modern replacement for the old "AsyncTask plus UI callback" pattern is often a coroutine launched from lifecycle-aware scope:
This example is shorter than the old AsyncTask version, and the intent is much clearer: do the slow work on an I/O dispatcher, then resume on the main thread to update the UI.
Even if your immediate question is about Handler versus Thread, this is why many Android engineers now answer with "neither, use coroutines" for new app code.
Common Pitfalls
- Treating
Handleras a substitute for background execution. It only schedules work on a thread that already exists. - Starting raw threads and then touching views directly from those threads.
- Using
AsyncTaskin new code even though it is deprecated and awkward around configuration changes. - Forgetting cancellation and lifecycle. Background work that outlives a destroyed
Activitycan leak memory or update the wrong screen. - Choosing a low-level primitive when the real requirement is scheduled work, structured concurrency, or persistent background processing.
Summary
- '
Threadruns background code but gives you little structure.' - '
Handlermoves messages or callbacks onto a thread with aLooper, often the main thread.' - '
AsyncTaskwas a convenience wrapper around background work plus UI callbacks, but it is deprecated.' - In legacy Android code, you may still see all three together because
Threaddoes work andHandlerpublishes results. - In new Android development, coroutines, executors, and
WorkManagerare usually better tools.
Related reading
- Handling asynchronous database queries in node.js and mongodb
- Handling exceptions from Java ExecutorService tasks
- Handling exceptions from Java ExecutorService tasks
- Handling interdependent and/or layered asynchronous calls
- Handling an empty UITableView. Print a friendly message
- Handling applicationDidBecomeActive - How can a view controller respond to the app becoming Active?
- Handling InterruptedException in Java
- Handling multiple returns asynchronously in node.js
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.