Android Development
Programming Solutions
Deprecation
Code Optimization
API Updates

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 itself is not deprecated. What is deprecated is the implicit constructor style such as new Handler() and new Handler(callback), because those forms silently pick the current thread's Looper and can attach your handler to the wrong thread or to no valid looper at all.

What Changed

The Android docs deprecate the zero-argument and callback-only constructors because implicit thread-local looper selection is error-prone. The modern fix is to make the looper explicit or use a higher-level concurrency tool that better matches the job.

In other words, replace this:

java
Handler handler = new Handler();

with something explicit like this:

java
Handler handler = new Handler(Looper.getMainLooper());

That one change already removes most of the ambiguity.

Posting Work to the Main Thread

If your goal is "run this on the UI thread," the modern direct replacement is an explicit main looper handler.

java
1import android.os.Handler;
2import android.os.Looper;
3
4Handler mainHandler = new Handler(Looper.getMainLooper());
5mainHandler.post(() -> {
6    System.out.println("Running on the main thread");
7});

This is still valid Android code. The key improvement is that the target thread is obvious.

Sometimes You Do Not Need Handler at All

Many older code samples used Handler as a generic async tool even when a better abstraction existed. If you are doing background work, prefer APIs that express background execution directly:

  • 'Executor or ExecutorService for Java concurrency'
  • Kotlin coroutines for modern Kotlin apps
  • 'WorkManager for guaranteed deferrable background work'

For example, this is often clearer than a background HandlerThread for one-off tasks:

java
1import java.util.concurrent.ExecutorService;
2import java.util.concurrent.Executors;
3
4ExecutorService executor = Executors.newSingleThreadExecutor();
5executor.execute(() -> {
6    System.out.println("Background work");
7});

When Handler Is Still the Right Tool

Handler still makes sense when you are interacting with an existing Looper and message queue. Typical examples include:

  • posting back to the main thread
  • interoperating with APIs that already use Message objects
  • working on a dedicated looper thread

If you truly need a handler on a non-main looper, create or obtain that looper explicitly.

java
1import android.os.Handler;
2import android.os.HandlerThread;
3
4HandlerThread thread = new HandlerThread("worker");
5thread.start();
6
7Handler workerHandler = new Handler(thread.getLooper());
8workerHandler.post(() -> {
9    System.out.println("Running on HandlerThread");
10});

This is much clearer than relying on implicit construction.

View and Activity Helpers Can Be Simpler

If the job is tied to a UI element, helper APIs may be better than managing a raw handler yourself.

java
button.post(() -> button.setText("Updated"));

Or in an Activity:

java
runOnUiThread(() -> {
    // update UI
});

These helpers communicate intent directly and avoid manual looper plumbing.

Async Handlers

If you need a handler whose messages are not blocked by synchronization barriers such as display vsync, Android also offers async handler creation.

java
Handler asyncHandler = Handler.createAsync(Looper.getMainLooper());

That is a specialized tool, not a general replacement for every handler use case, but it is worth knowing when UI scheduling behavior matters.

Common Pitfalls

The biggest pitfall is thinking Handler has been deprecated entirely. It has not. The problem is the implicit constructor style, not the entire class.

Another issue is replacing every Handler with an executor without checking whether the code actually needs a looper-backed message queue. Executors run tasks, but they are not message-loop primitives.

Developers also keep using new Handler() in utility code that may run on threads without loopers. That is exactly the kind of fragile behavior the deprecation was meant to surface.

Finally, do not use Handler as a blanket answer for long-running or guaranteed background work. For that, WorkManager or other lifecycle-aware tools are often more appropriate.

Summary

  • 'Handler is still valid; only the implicit constructors such as new Handler() are deprecated.'
  • Use new Handler(Looper.getMainLooper()) when you need to post to the UI thread.
  • Use HandlerThread plus an explicit looper only when you truly need a looper-backed worker thread.
  • Prefer executors, coroutines, or WorkManager when the problem is general concurrency rather than message-loop scheduling.
  • Make the target thread explicit so the code is predictable and easier to maintain.

Course illustration
Course illustration

All Rights Reserved.