Handler deprecated [duplicate] - java

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)

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)
Source : developer.android.com

From API level 30, there are 2 constructors are deprecated.
Handler()
Handler(Handler.Callback)
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.
Handler(Looper)
Handler(Looper, Handler.Callback)
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
})

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.

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:
Specify the Looper explicitly:
Handler(Looper.getMainLooper()).postDelayed({
// code
}, duration)
Specify the implicit thread local behavior:
Handler(Looper.myLooper()!!).postDelayed({
// code
}, duration)
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);

Provide a looper in the Handler Constructor
Handler(Looper.getMainLooper())

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
}

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)
}

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);
}

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.

I usually use this one
Code:
Handler(Looper.myLooper() ?: return).postDelayed({
// Code what do you want
}, 3000)
Screenshot:

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...

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

How do I avoid a handler.postDelayed(Runnable run) from being called?

I have this method to scan Bluetooth LE devices. The scanner runs asynchronously for 10s and then it is interrupted.
public void startScanning() {
Handler handler = new Handler();
final long SCAN_PERIOD = 10000;
handler.postDelayed(new Runnable() {
#Override
public void run() {
btScanner.stopScan(leScanCallback);
}
}, SCAN_PERIOD);
btScanner.startScan(leScanCallback);
}
However, depending on a condition that is verified during the scan (for example, I find a device I was looking for, etc.), I call btScanner.stopScan(leScanCallback). So I don't want to call the stopScan after SCAN_PERIOD otherwise I'd call it twice. How do I avoid the second call?
Try to remove call back:
handler.removeCallbacksAndMessages(null);
Handler handler = new Handler();
Runnable runnableRunner = new Runnable() {
#Override
public void run() {
btScanner.stopScan(leScanCallback);
}
}
public void startScanning() {
final long SCAN_PERIOD = 10000;
handler.postDelayed(runnableRunner, SCAN_PERIOD);
btScanner.startScan(leScanCallback);
}
Use removeCallbacks removes any pending posts of Runnable r that are in the message queue.
// cancel runnable whenever your condition is met.
handler.removeCallbacks(runnableRunner);
or use to remove all messages and callbacks
handler.removeCallbacksAndMessages(null);
I have another question on this problem.
I have a method m() in my "sequential" part of the code, not in the asynchronous one, that I need to call only if either the handler.removeCallbacksAndMessages(null); is called, or after the SCAN_PERIOD has expired. How do I check these conditions and basically wait that one of the two happens? Do I need to put m() in a synchronous run?
(Now I also have the global handler that I can use)

Android - How to stop the runnable and handler method

In my application i will be keep on updating some info in some time interval.so i have done like this
handler = new Handler();
and then some Task
handler.postDelayed(runLocation, 1000);
public Runnable runLocation = new Runnable(){
#Override
public void run() {
MainActivity.this.handler.postDelayed(MainActivity.this.runLocation, 100);
};
My problem is i want to stop this runnable at some point of time.how to do this ?
Can you help me?
You can use removeCallbacks. Just call
handler.removeCallbacks(runLocation);
it will remove any pending items in the message's queue.
Use removeCallbacks
#Override
public void run() {
if(isStopHandler){
MainActivity.this.handler.removeCallbacks(this);
return;
}
// do your runnable work
// set isStopHandler = true when needed so next time this method is executed, it will get inside if cond.
};

Getting reference to a Handler when using ExecutorService

I am using a ScheduledExecutorService to run a Runnable periodically. In the Runnable I have registered a SensorEventListener. I noticed that the SensorEventListener callbacks gets called on the main thread rather than a thread from the thread pool of the ScheduledExecutorService. I want to handle the callbacks for the sensor events off of the main thread. It looks like I am able to pass a Handler object when calling registerListener() of the SensorManager class and the callbacks will be run on the thread that the Handler is from.
Is there a way to get a reference to a Handler of a ScheduledExecutorService?
It's hard. A regular ExecutorService does not have a Looper and you can not have a Handler in such a thread.
A Looper is an infinite loop that dequeues and executes events. So if you schedule that your executor is blocked. You can probably implement your own executor based on the event handling of a Looper but I guess that's not what you want.
To use the sensor from the background you would create a HandlerThread. That's a background thread running a Looper and therefore it can have a Handler.
Small example
private HandlerThread mHandlerThread;
private Handler mBackgroundHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandlerThread = new HandlerThread("SomeNameHere");
mHandlerThread.start();
mBackgroundHandler = new Handler(mHandlerThread.getLooper());
mBackgroundHandler.post(new Runnable() {
#Override
public void run() {
// I can do blocking network here.
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quitSafely();
}
You can try Handler handler = new Handler(Looper.myLooper()); in the background thread (Where your ScheduledExecutorService is running) and then pass the instance to the SensorManager.
Handler always 'handles' thing in the thread it was created, so just create a new one in your ExecutorService. Just use default Handler constructor

Capturing executor for current thread

I'm using ListenableFuture from Guava, and one nice thing about them is that one pass Executor to the Futures.addCallback method, that is, ask to execute the callback on a given thread/executor.
In my Android application, I want to be able to start the asynchronous execution based on ListenableFuture in the UI thread, and schedule a callback which is also executed also on the UI thread. Therefore, I'd like to somehow submit the UI thread executor to the Futures.addCallback method mentioned above. How to achieve that?
Or, in other words, I want to have an executor for the UI thread. Is it available already in Android, or, if I have to create my own, how do I do that?
EDIT: As an extension to this question, is it possible to do same thing, but not just with UI thread, but with any particular thread, where the call to async method is made?
I would be happy to know how to achieve the same effect without resorting to the Android-specific stuff like Handler and Looper, just with pure Java.
I think I've see some implementation doing that. The basic Idea is roughly
class UiThreadExecutor implements Executor {
private final Handler mHandler = new Handler(Looper.getMainLooper());
#Override
public void execute(Runnable command) {
mHandler.post(command);
}
}
You can delegate to run anything in the main thread by passing it to a handler for the main thread.
Edit: https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit/android/MainThreadExecutor.java for example
Edit2: You can configure the handler like e.g. SensorManager#registerListener(..., Handler handler) allows you to do.
class HandlerThreadExecutor implements Executor {
private final Handler mHandler;
public HandlerThreadExecutor(Handler optionalHandler) {
mHandler = optionalHandler != null ? optionalHandler : new Handler(Looper.getMainLooper());
}
#Override
public void execute(Runnable command) {
mHandler.post(command);
}
}
The advantage over using the current thread's looper is that it makes it explicit which Looper you use. In your solution you take the Looper of whatever thread calls new ExecuteOnCaller() - and that's often not the thread you run code in later.
I would be happy to know how to achieve the same effect without resorting to the Android-specific stuff like Handler and Looper, just with pure Java.
Looper, Handler and the message queue behind all that logic are made of mostly pure Java. The problem with a generic solution is that you can't "inject" code to run into a thread. The thread must periodically check some kind of task queue to see if there is something to run.
If you write code like
new Thread(new Runnable() {
#Override
public void run() {
while (!Thread.interrupted()) {
System.out.println("Hello");
}
}
}).start();
Then there is no way to make that thread do anything else but constantly print "Hello". If you could do that it would be like dynamically inserting a jump to other code into the program code. That would IMO be a terrible idea.
final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
new Thread(new Runnable() {
#Override
public void run() {
try {
while (true) {
Runnable codeToRunInThisThread = queue.take();
codeToRunInThisThread.run();
}
} catch (InterruptedException ignored) {}
}
}).start();
On the other hand is a simple thread that loops forever on a queue. The thread could do other tasks in between but you have to add a manual check into the code.
And you can send it tasks via
queue.put(new Runnable() {
#Override
public void run() {
System.out.println("Hello!");
}
});
There is no special handler defined here but that's the core of what Handler & Looper do in Android. Handler in Android allows you to define a callback for a Message instead of just a Runnable.
Executors.newCachedThreadPool() and similar do roughly the same thing. There are just multiple threads waiting on code in a single queue.
As an extension to this question, is it possible to do same thing, but not just with UI thread, but with any particular thread, where the call to async method is made?
The generic answer is No. Only if there is a way to inject code to run in that thread.
Based on asnwer from #zapl, here is my implementation, which also answers the edited (extended) question: https://gist.github.com/RomanIakovlev/8540439
Figured out I'll also put it here, in case if link will rot some day:
package com.example.concurrent;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;
/**
* When the calling thread has a Looper installed (like the UI thread), an instance of ExecuteOnCaller will submit
* Runnables into the caller thread. Otherwise it will submit the Runnables to the UI thread.
*/
public class ExecuteOnCaller implements Executor {
private static ThreadLocal<Handler> threadLocalHandler = new ThreadLocal<Handler>() {
#Override
protected Handler initialValue() {
Looper looper = Looper.myLooper();
if (looper == null)
looper = Looper.getMainLooper();
return new Handler(looper);
}
};
private final Handler handler = threadLocalHandler.get();
#Override
public void execute(Runnable command) {
handler.post(command);
}
}
My pattern to use it would be like this:
/**
* in SomeActivity.java or SomeFragment.java
*/
Futures.addCallback(myModel.asyncOperation(param), new FutureCallback<Void>() {
#Override
public void onSuccess(Void aVoid) {
// handle success
}
#Override
public void onFailure(Throwable throwable) {
// handle exception
}
}, new ExecuteOnCaller());
Use com.google.android.gms.tasks.TaskExecutors.MAIN_THREAD.
An Executor that uses the main application thread.
Source: Android docs
The tasks APIs are part of Google Play services since version 9.0.0.
For Android UI thread executor use:
ContextCompat.getMainExecutor(context)
To address your question and extended question to create an Executor that simply runs on the current thread and avoids Android classes:
class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}
See documentation: https://developer.android.com/reference/java/util/concurrent/Executor

Looper.loop not called but code in handler is still executed

I am trying to understand how looper.loop works, and when to use it in my code.
I have a bound service that has a runnable. Inside I have a handler that is used to hold code that updates the Ui. Here is the code.
private Runnable builder = new Runnable() {
public void run()
{
while(isThreadStarted == true)
{
if (newgps == 1)
{
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
//some code that updates UI
}
}
}
}
}
looper.prepair
}
From what I read looper. prepare has to be called before the handler is made. But I noticed I didn’t do that but this code still works. How is this possible?
I want to fix this but im not sure where to put loper.loop. Because this handler is called many many times. Would it be ok if i put looper.loop right after .prepare? Then .quit when isThreadStarted == false?
The Handler is associated with the UI thread, since you attach it to Looper.getMainLooper(). The UI thread prepares its Looperon application start, so that is nothing the application has to do itself.
By using Looper.getMainLooper(), you get Handler of main thread and you are posting on main thread. Main thread has its looper prepared by system already when it is created.

Categories

Resources