Safe delay in Android Views: goodbye Handlers, Hello Coroutines!
Using Handlers, without providing a Looper, has been deprecated in recent versions of Android because they can lead to bugs where operations are silently lost or crashes. According to the documentation, you can use an Executor or access the Handler of a view as a replacement, but they can also crash silently as it’s not bound to any lifecycle.
Let’s see how we can take advantage of the coroutines and lifecycle libraries to replace the Handler().postDelay() method to safely trigger an action after a specified amount of time.
Handler: old fashioned way
Even if the Handler() constructor is deprecated, you can access the handler of a View by calling getHandler() or you can directly call the method postDelay() on the View. That way, you won’t use any deprecated method and you will be able to delay you action with a given amount of time.
Let’s see how it works under the hood. To push new events, you will get access to an Handler that is associated with the thread running the view. Every time a new action will be posted with postDelay(), a new Runnable will be added to the message queue (remember that the runnable will be running on the UI thread).
By doing this, your delayed action may run when the view is detached or destroyed and this can lead to unexpected behaviors or crashes.
Kotlin + Coroutine + Lifecycle 💚
Let’s see how we can implement a better solution than Handler().postDelay() using Kotlin, Coroutines and the lifecycle library. First, make sure that you have these dependencies in your project:
Then, we are ready to implement the function. To achieve a safe delay, we’ll need to find the lifecycleOwner of the view thanks to the findViewTreeLifecycleOwner() method provided by the lifecycle library.
Then, we can access the coroutine scope of the lifecycle to launch a new coroutine with a given Dispatcher (we’ll be using the Main Dispatcher by default as we want to perform actions in the UI thread). To trigger the work in the coroutine in a specified amount of time, we’ll apply a delay() method before calling our main block function.
That way, we can safely run some delayed tasks in our views as they will be tied to the lifecycle.
Now, you can use this function to safely delay some actions attached to your views as the following:
It has always been painful to manage multithreaded actions on Android as you must take into account the lifecycle of an Activity, Fragment and so on. But thanks to the coroutines and the lifecycle libraries we have now something more reliable to handle such cases.
Big thanks to my teammate Olivier Buiron who has brought this wonderful extension function to life 🥳
Do not hesitate to ping me on Twitter if you have any question 🤓