If you are using a Handler to execute delayed actions with postDelayed() you can run into troubles when the execution of the action happens after your Activity or Fragment has been destroyed.
There is a simple solution to this. Bind your Handler to the lifecycle.
Create a LifecycleObserver
First lets create a LifecycleObserver that gets a Handler instance.
In the event of Lifecycle.Event.ON_DESTROY it will remove all callbacks and messages from that Handler.
class LifecycleObververHandler(private val handler: Handler) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
internal fun onDestroy() {
handler.removeCallbacksAndMessages(null)
}
}
Add the LifecycleObserver to the LifecycleOwner
Next we have to add the LifecycleObververHandler to a LifecycleOwner. We also wanna create these lifecycle observed handlers easily. So lets create a LifecycleHandlerFactory.
That factory gets created with a lambda handlerFactory that gives you an instance of a Handler (default is a Handler with a main Looper). It has one function create that expects a LifecycleOwner.
Within that function it checks that the state of the Lifecycle is not DESTROYED. It calls the handlerFactory to get an instance of Handler. Then it creates a LifecycleObserverHandler, which takes the handler, and adds that Observer to the LifecycleOwner. Finally the Handler gets returned.
class LifecycleHandlerFactory(private val handlerFactory: (() -> Handler) = { Handler(Looper.getMainLooper()) }) {
fun create(owner: LifecycleOwner): Handler {
check(owner.lifecycle.currentState != Lifecycle.State.DESTROYED) {
"Cannot create a Handler for a destroyed life-cycle"
}
val handler = handlerFactory.invoke()
val observer = LifecycleObververHandler(handler)
owner.lifecycle.addObserver(observer)
return handler
}
}
Inject the lifecycle aware Handler
When you are using a DependendencyInjection Framework or a service locater like Koin you can inject the lifecycle aware Handler.
module {
// a single instance of LifecycleHandlerFactory
// it gets a lambda that every time its being called returnes a new Handler with a main looper.
single { LifecycleHandlerFactory() }
// uses the LifecycleHandlerFactory to create a new handler with a LifecycleOwner as parameter.
factory<Handler> { (lifecycleOwner: LifecycleOwner) -> get<LifecycleHandlerFactory>().create(lifecycleOwner) }
}
Finally you can inject a lifecycle handler in your Fragment (or Activity).
// injects a new handler with a LifecycleOwner as a parameter
private val handler: Handler by inject { parametersOf(viewLifecycleOwner) }