Why toasts fired on a coroutine are displayed even when the coroutine is cancelled?

226 Views Asked by At

I have an application that displays a lot of Toasts, as a result Toasts are displayed long after the application went on background.

In order to cancel all Toasts when the application is in background, I have implemented a custom class to wrap the calls to Toast.makeText into a single call done on a coroutine. My idea was that when the onPause method of an activity is fired then I could call CoroutineContext.cancel() to cancel all coroutine. By doing so I hoped that all Toast would have been canceled.

class CustomToast  {
    val job  = Job()
    val coroutineContext = Dispatchers.Main + job
    val coroutineScope = CoroutineScope(coroutineContext)

    fun showSuspendToast(context: Context, msg: String) {
        coroutineScope.launch {
            Toast.makeText(context, "FROM CustomTEST | $msg", Toast.LENGTH_LONG).show()
        }
    }

    fun cancelToastCoroutines() {
        coroutineContext.cancelChildren()
    }
}

Obviously it does not work.

Why does it not work? Where is the error in my reasoning?

2

There are 2 best solutions below

4
Agent_L On BEST ANSWER

Toast.makeText is non-blocking. Toasts are queued by the system. Therefore, all the Toast.makeText are long finished when you get onPause. Cancelling the coroutine does nothing because the coroutine isn't doing anything when you cancel it.

You should add a Log after Toast.makeText, that would visualize to you when the toast are scheduled.

1
Tenfour04 On

Your minSdkVersion is probably lower than 30, but just in case it is 30 or higher, you could create a function that shows the toast in a suspending cancellable way so that your strategy could work. This uses Toast callbacks, which were added in SDK 30.

suspend fun Toast.showCancellable() = suspendCancellableCoroutine { continuation ->
    val callback = object: Toast.Callback {
        override fun onToastShown() {}
        
        override fun onToastHidden() {
            removeCallback(this)
            continuation.resume(Unit) 
        }
    }
    addCallback(callback)
    show()
    invokeOnCancellation { 
        removeCallback(callback)
        cancel()
    }
}