What do I use now that Handler() is deprecated?

Viewed 129247

How do I fix the deprecation warning in this code? Alternatively, are there any other options for doing this?

Handler().postDelayed({
    context?.let {
        //code
    }
}, 3000)
20 Answers

Only the parameterless constructor is deprecated, it is now preferred that you specify the Looper in the constructor via the Looper.getMainLooper() method.

Use it for Java

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        // Your Code
    }
}, 3000);

Use it for Kotlin

Handler(Looper.getMainLooper()).postDelayed({
    // Your Code
}, 3000)

If you want to avoid the null check thing in Kotlin (? or !!) you can use Looper.getMainLooper() if your Handler is working with some UI related thing, like this:

Handler(Looper.getMainLooper()).postDelayed({
   Toast.makeText(this@MainActivity, "LOOPER", Toast.LENGTH_SHORT).show()
}, 3000)

Note: use requireContext() instead of this@MainActivity if you are using fragment.

From API level 30, there are 2 constructors are deprecated.

Google explains the reason below.

Implicitly choosing a Looper during Handler construction can lead to bugs where operations are silently lost (if the Handler is not expecting new tasks and quits), crashes (if a handler is sometimes created on a thread without a Looper active), or race conditions, where the thread a handler is associated with is not what the author anticipated. Instead, use an Executor or specify the Looper explicitly, using Looper#getMainLooper, {link android.view.View#getHandler}, or similar. If the implicit thread local behavior is required for compatibility, use new Handler(Looper.myLooper(), callback) to make it clear to readers.

Solution 1: Use an Executor

1. Execute code in the main thread.

Java

// Create an executor that executes tasks in the main thread. 
Executor mainExecutor = ContextCompat.getMainExecutor(this);

// Execute a task in the main thread
mainExecutor.execute(new Runnable() {
    @Override
    public void run() {
        // You code logic goes here.
    }
});

Kotlin

// Create an executor that executes tasks in the main thread.
val mainExecutor = ContextCompat.getMainExecutor(this)

// Execute a task in the main thread
mainExecutor.execute {
    // You code logic goes here.
}

2. Execute code in a background thread

Java

// Create an executor that executes tasks in a background thread.
ScheduledExecutorService backgroundExecutor = Executors.newSingleThreadScheduledExecutor();

// Execute a task in the background thread.
backgroundExecutor.execute(new Runnable() {
    @Override
    public void run() {
        // Your code logic goes here.
    }
});

// Execute a task in the background thread after 3 seconds.
backgroundExecutor.schedule(new Runnable() {
    @Override
    public void run() {
        // Your code logic goes here
    }
}, 3, TimeUnit.SECONDS);

Kotlin

// Create an executor that executes tasks in a background thread.
val backgroundExecutor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor()

// Execute a task in the background thread.
backgroundExecutor.execute {
    // Your code logic goes here.
}

// Execute a task in the background thread after 3 seconds.
backgroundExecutor.schedule({
    // Your code logic goes here
}, 3, TimeUnit.SECONDS)

Note: Remember to shut down the executor after using.

backgroundExecutor.shutdown(); // or backgroundExecutor.shutdownNow();

3. Execute code in a background thread and update UI on the main thread.

Java

// Create an executor that executes tasks in the main thread. 
Executor mainExecutor = ContextCompat.getMainExecutor(this);

// Create an executor that executes tasks in a background thread.
ScheduledExecutorService backgroundExecutor = Executors.newSingleThreadScheduledExecutor();

// Execute a task in the background thread.
backgroundExecutor.execute(new Runnable() {
    @Override
    public void run() {
        // Your code logic goes here.
        
        // Update UI on the main thread
        mainExecutor.execute(new Runnable() {
            @Override
            public void run() {
                // You code logic goes here.
            }
        });
    }
});

Kotlin

// Create an executor that executes tasks in the main thread. 
val mainExecutor: Executor = ContextCompat.getMainExecutor(this)

// Create an executor that executes tasks in a background thread.
val backgroundExecutor = Executors.newSingleThreadScheduledExecutor()

// Execute a task in the background thread.
backgroundExecutor.execute {
    // Your code logic goes here.

    // Update UI on the main thread
    mainExecutor.execute {
        // You code logic goes here.
    }
}

Solution 2: Specify a Looper explicitly by using one of the following constructors.

1. Execute code in the main thread

1.1. Handler with a Looper

Java

Handler mainHandler = new Handler(Looper.getMainLooper());

Kotlin

val mainHandler = Handler(Looper.getMainLooper())

1.2 Handler with a Looper and a Handler.Callback

Java

Handler mainHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message message) {
        // Your code logic goes here.
        return true;
    }
});

Kotlin

val mainHandler = Handler(Looper.getMainLooper(), Handler.Callback {
    // Your code logic goes here.
    true
})

2. Execute code in a background thread

2.1. Handler with a Looper

Java

// Create a background thread that has a Looper
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

// Create a handler to execute tasks in the background thread.
Handler backgroundHandler = new Handler(handlerThread.getLooper()); 

Kotlin

// Create a background thread that has a Looper
val handlerThread = HandlerThread("HandlerThread")
handlerThread.start()


// Create a handler to execute tasks in the background thread.
val backgroundHandler = Handler(handlerThread.looper)

2.2. Handler with a Looper and a Handler.Callback

Java

// Create a background thread that has a Looper
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

// Create a handler to execute taks in the background thread.
Handler backgroundHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message message) {
        // Your code logic goes here.
        return true;
    }
});

Kotlin

// Create a background thread that has a Looper
val handlerThread = HandlerThread("HandlerThread")
handlerThread.start()


// Create a handler to execute taks in the background thread.
val backgroundHandler = Handler(handlerThread.looper, Handler.Callback {
    // Your code logic goes here.
    true
})

Note: Remember to release the thread after using.

handlerThread.quit(); // or handlerThread.quitSafely();

3. Execute code in a background thread and update UI on the main thread.

Java

// Create a handler to execute code in the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

// Create a background thread that has a Looper
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

// Create a handler to execute in the background thread
Handler backgroundHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message message) {
        // Your code logic goes here.
        
        // Update UI on the main thread.
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                
            }
        });
        
        return true;
    }
});

Kotlin

// Create a handler to execute code in the main thread
val mainHandler = Handler(Looper.getMainLooper())

// Create a background thread that has a Looper
val handlerThread = HandlerThread("HandlerThread")
handlerThread.start()

// Create a handler to execute in the background thread
val backgroundHandler = Handler(handlerThread.looper, Handler.Callback {
    // Your code logic goes here.

    // Update UI on the main thread.
    mainHandler.post {
        
    }
    true
})

The deprecated function is that constructor for Handler. Use Handler(Looper.myLooper()) .postDelayed(runnable, delay) instead

Consider using coroutines

scope.launch {
    delay(3000L)
    // do stuff
}

Using lifecycle scope this is more easy. Inside activity or fragment.

 lifecycleScope.launch {
     delay(2000)
     // Do your stuff
 }

or use handler

        Handler(Looper.myLooper()!!)

I have 3 solutions:

  1. Specify the Looper explicitly:
    Handler(Looper.getMainLooper()).postDelayed({
        // code
    }, duration)
    
  2. Specify the implicit thread local behavior:
    Handler(Looper.myLooper()!!).postDelayed({
        // code
    }, duration)
    
  3. using Thread:
    Thread({
        try{
            Thread.sleep(3000)
        } catch (e : Exception) {
            throw e
        }
         // code
    }).start()
    

Handler() and Handler(Handler.Callback callback) constructors are deprecated. Because those can leads to bugs & crashes. Use Executor or Looper explicitly.

For Java

Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //do your work here
   }
}, 1000);

use this

Looper.myLooper()?.let {
    Handler(it).postDelayed({
        //Your Code
    },2500)
}

Use Executor instead of handler for more info Executor.
To achieve post delay use ScheduledExecutorService:

ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
Runnable runnable = () -> {
    public void run() {
        // Do something
    }
};
worker.schedule(runnable, 2000, TimeUnit.MILLISECONDS);
import android.os.Looper
import android.os.Handler

inline fun delay(delay: Long, crossinline completion: () -> Unit) {
    Handler(Looper.getMainLooper()).postDelayed({
        completion()
    }, delay)
}

Example:

delay(1000) {
    view.refreshButton.visibility = View.GONE
}

Provide a looper in the Handler Constructor

Handler(Looper.getMainLooper())

If you are using Variable for Handler and Runnable then use it like this.

private Handler handler;
private Runnable runnable;

handler = new Handler(Looper.getMainLooper());
    handler.postDelayed(runnable = () -> {
        // Do delayed stuff here
         handler.postDelayed(runnable, 1000);
    }, delay);

Also You need to remove callbacks in onDestroy()

@Override
public void onDestroy() {
    super.onDestroy();
    if (handler != null) {
        handler.removeCallbacks(runnable);
    }
}

Coroutines Kotlin

private val SPLASH_SCREEN_TIME_OUT_CONST: Long = 3000

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_splash)
    window.setFlags(
        WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN
    )
    GlobalScope.launch {
        delay(SPLASH_SCREEN_TIME_OUT_CONST)
        goToIntro()
    }

}

private fun goToIntro(){
    startActivity(Intent(this, IntroActivity::class.java))
    finish()
}

It's a good idea use this structure in Kotlin

companion object Run {
   fun after(delay: Long, process: () -> Unit) {
      Handler(Looper.getMainLooper()).postDelayed({
          process()
      }, delay)
   }
}

Later call as

Run.after(SPLASH_TIME_OUT) {
   val action = SplashFragmentDirections.actionSplashFragmentToLogin()
   v.findNavController().navigate(action)
}

According to the document (https://developer.android.com/reference/android/os/Handler#Handler()):

Implicitly choosing a Looper during Handler construction can lead to bugs where operations are silently lost (if the Handler is not expecting new tasks and quits), crashes (if a handler is sometimes created on a thread without a Looper active), or race conditions, where the thread a handler is associated with is not what the author anticipated. Instead, use an Executor or specify the Looper explicitly, using Looper#getMainLooper, {link android.view.View#getHandler}, or similar. If the implicit thread local behavior is required for compatibility, use new Handler(Looper.myLooper()) to make it clear to readers.

We should stop using the constructor without a Looper, and specify a Looper instead.

Java Answer

I wrote a method to use easily. You can use this method directly in your project. delayTimeMillis can be 2000, it means that this code will run after 2 seconds.

private void runJobWithDelay(int delayTimeMillis){
    new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
        @Override
        public void run() {
            //todo: you can call your method what you want.
        }
    }, delayTimeMillis);
}

The handler() etc code is generated by the Android Studio 4.0.1 when a Fullscreen Activity, for example, is created from scratch. I know that we are being encouraged to use Kotlin, which I do, but from time to time I use sample projects to get an idea going. It seems strange that we are chastised by AS when AS actually generates the code. It might be a useful academic activity to go through the errors and fix them but maybe AS could generate new clean code for us enthusiasts...

I usually use this one

Code:

Handler(Looper.myLooper() ?: return).postDelayed({
           // Code what do you want
        }, 3000)

Screenshot:

enter image description here

For Xamarin Android, instead of

Handler handler;
handler = new Handler();

just write

Handler handler;
handler = new Handler(Looper.MyLooper());

the rest of the code is fine.

Related