Deprecation
Handler
Programming
Alternatives
Software Development

What do I use now that Handler is deprecated?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

On Android, Handler as a concept is not fully deprecated. The part that commonly triggers warnings is the no-argument constructor, because it implicitly relies on the current thread's Looper, which makes thread targeting too easy to get wrong.

What Is Actually Deprecated

Code like this is the usual problem:

kotlin
val handler = Handler()

That constructor hides which thread will receive the posted work. If the current thread does not have the expected Looper, the behavior can be wrong or fragile.

The direct replacement is to pass an explicit Looper:

kotlin
1import android.os.Handler
2import android.os.Looper
3
4val mainHandler = Handler(Looper.getMainLooper())
5mainHandler.post {
6    println("Running on the main thread")
7}

This makes the target thread explicit and removes the ambiguity that caused the deprecation warning.

Use Higher-Level APIs When They Match the Job Better

Even though Handler is still valid in some cases, modern Android code often has better options depending on intent.

If you are updating a View, use the view itself:

kotlin
button.post {
    button.text = "Updated"
}

If you need general background work, use an executor:

kotlin
1import java.util.concurrent.Executors
2
3val executor = Executors.newSingleThreadExecutor()
4executor.execute {
5    println("Background work")
6}

If your project uses Kotlin coroutines, that is often the clearest option:

kotlin
1import kotlinx.coroutines.CoroutineScope
2import kotlinx.coroutines.Dispatchers
3import kotlinx.coroutines.launch
4
5CoroutineScope(Dispatchers.Main).launch {
6    println("UI work from coroutine")
7}

These APIs usually express intent more clearly than a raw message queue.

Main-Thread Execution Has Better Dedicated Options Too

If your only goal is to run something on the main thread, newer Android APIs can express that more directly than a generic handler. For example, a main-thread executor makes the destination explicit without exposing message-queue details:

kotlin
1val executor = context.mainExecutor
2executor.execute {
3    println("Running on the main thread")
4}

This is often a better fit for one-off UI dispatch than carrying a long-lived handler field around an entire class.

Use HandlerThread Only When You Need a Looper Thread

Some older APIs still expect a looper-backed thread. In that situation, HandlerThread plus a Handler is still legitimate.

kotlin
1import android.os.Handler
2import android.os.HandlerThread
3
4val workerThread = HandlerThread("worker-thread")
5workerThread.start()
6
7val workerHandler = Handler(workerThread.looper)
8workerHandler.post {
9    println("Work running on dedicated looper thread")
10}

This pattern is still useful when you truly need a message queue on a dedicated thread. But if you only need background execution, an executor or coroutine dispatcher is often simpler.

Think in Terms of Intent

A good migration rule is:

  • use Handler(Looper.getMainLooper()) when you genuinely need handler-style posting on the main thread
  • use View.post for view-related UI scheduling
  • use executors for general background tasks
  • use coroutines when the project is already coroutine-based
  • use WorkManager for durable deferred work that should survive process death

This is a better question than "What replaces Handler everywhere?" because there is no one universal replacement. The right answer depends on why the old handler existed.

Common Pitfalls

  • Assuming the entire Handler API is obsolete when the main issue is the implicit constructor.
  • Replacing Handler() with another API without making the target thread clearer.
  • Using Handler(Looper.getMainLooper()) for heavy background work.
  • Creating a HandlerThread when a plain executor would be simpler and easier to maintain.
  • Scheduling durable background work with a Handler when the task really belongs in WorkManager.

Summary

  • The common deprecation issue is the no-argument Handler() constructor, not the entire Handler type.
  • If you still need a handler, create it with an explicit Looper.
  • Prefer higher-level tools such as View.post, executors, coroutines, or WorkManager when they better match the job.
  • Choose the replacement based on intent, not just on API name similarity.
  • The safest modernization step is to make thread ownership explicit everywhere.

Course illustration
Course illustration

All Rights Reserved.