1. Overview
In this article, we will see the deprecated constructors of the handler and its alternative solutions in Android.
Normally, Android apps use the main thread to handle UI tasks and input events. So executing any long-running tasks on this main thread can lead to app freezes and unresponsiveness. This main thread collects the UI events or messages in a queue (MessageQueue
) and then processes them using an instance of the Looper class. By default, the main thread already has a Looper prepared.
You can also create a thread with a looper.
This Looper
helps to keep the thread alive and manages a message queue to execute tasks on that thread. It takes each message from the queue and executes it. A Handler gives you a mechanism to push tasks into this queue from any other threads. You can use Handler to add a Runnable object to the Looper to execute the code on the thread associated with the Looper.
We associate each Handler instance with a single thread and that thread’s message queue. Whenever you create a new Handler instance, it is tied up to a Looper
. It will deliver messages and Runnables to that Looper’s message queue and execute them on that Looper’s thread.
In addition to Handler, Android supports the Thread class to perform asynchronous processing. Besides, it also supports the java.util.concurrent
package to perform the background tasks such as ThreadPool
and Executor
. See our articles to know more about various Android concepts.
2. Deprecated constructors of the handler Android
Only the following constructors of the Handler are deprecated.
Handler()
Handler(Handler.Callback)
The above constructors implicitly choose the local thread and its associated Looper to execute the background tasks. This can lead to bugs such as :
- Tasks lost silently (if the Handler is not expecting new tasks and quits)
- Crashes (it invokes the handler on a thread without a Looper active)
- Race conditions
- No control on the thread on which the handler executes.
To avoid using these deprecated constructors, Android suggests using the following solutions based on our use case:
- Use an Executor
- Specify the Looper explicitly.
Looper.getMainLooper()
to retrieve and use the Looper of the main thread- If you require the implicit thread-local behavior for compatibility, use
new Handler(Looper.myLooper(), callback)
.
The constructor new Handler(Looper.myLooper(), callback)
is the exact alternative to the aforementioned deprecated methods. All these use the local thread to execute the background task.
3. Alternative to deprecated handler Android
Let’s see examples for each of these solutions.
3.1. Specify Looper explicitly to the Looper
You can use the Handler
to enqueue an action to be performed on a different thread. To specify the thread on which to run the action, construct the Handler
using a Looper
for the thread. A Looper
is an object that runs the message loop for an associated thread.
Once you’ve created a Handler
, you can then use the post(Runnable)
method to post the message to the Looper and later runs the Runnable block of code in the corresponding thread.
3.1.1. Run handler in main thread
Looper
includes a helper function, getMainLooper()
, which retrieves the Looper
of the main thread. You can run code in the main thread by using this Looper
to create a Handler
.
val mainHandler = Handler(Looper.getMainLooper()).post { System.out.println("Thread : " + Thread.currentThread().name) }
2.1.2. Run handler in the current thread
To execute the handler in the current thread, then you have to retrieve the Looper of the current thread by using the Looper.myLooper
method. The constructor new Handler(Looper.myLooper(), callback)
is the exact alternative to the aforementioned deprecated methods. All these use the local thread to execute the background task.
val mainHandler = Handler(Looper.myLooper()).post { System.out.println("Thread : " + Thread.currentThread().name) }
3.1.3. Specify the Looper of any thread
Alternatively, you can create a thread with Looper and use it to execute the background task.
val handlerThread = HandlerThread("HandlerThread"); handlerThread.start(); val backgroundHandler = Handler(handlerThread.looper).post { println("Thread : " + Thread.currentThread().name) handlerThread.quitSafely(); }
3.1.4. Update main thread from another thread
You can create another Handler to update the UI thread from your background thread:
val backgroundHandler = Handler(handlerThread.getLooper(), Handler.Callback() { Handler(Looper.getMainLooper()).post { System.out.println("Thread : " + Thread.currentThread().name) } return true } })
3.2. Use an Executor
3.2.1. Run executor in main thread
You can get the executor that can run tasks in the main thread by using the ContextCompat.getMainExecutor(this)
.
val mainExecutor: Executor = ContextCompat.getMainExecutor(this) mainExecutor.execute(Runnable { })
3.2.2. Run executor in a background thread
You can create an executor to execute the tasks in a background thread.
val backgroundExecutor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor() backgroundExecutor.execute { backgroundExecutor.shutdown(); } backgroundExecutor.schedule({ backgroundExecutor.shutdown(); }, 5, TimeUnit.SECONDS)
3. Conclusion
To sum up, we have seen the constructors that are deprecated in the Handler class and the alternative solutions with examples.
Pingback: Handler in Kotlin - TedBlob
Pingback: Android Kotlin - Handler and Runnable examples - TedBlob
Pingback: Handler Handler New Handler? Quick Answer - Ko.taphoamini.com