Search code examples
androidkotlinkotlinx.coroutines

How to make the UI dispatcher resume the continuation immediately


I've been heavily using coroutines in my Android project. For example, I use them to hand off blocking calls and CPU-intensive tasks to a threadpool. This is where launch(UI) works perfectly: it ensures the continuation resumes on the UI thread.

I'm also using coroutines for stuff like awaiting for the first onDraw call, resuming the animation loop with a postOnAnimate(), etc. In these cases the UI context falls short of what I need because it resumes the continuation asynchronously, by submitting an item to the event loop, even though cont.resume() is called on the UI thread.

This has become particularly bad in the case where I await the first onDraw call on an ImageView: I have to reposition the image based on the now-known view dimensions. However, since the coroutine resumes asynchronously, onDraw() completes, then a few more run, and only then does my coroutine resume. The user experiences flicker because the improperly positioned image briefly appears.

Is there an option to force the UI dispatcher to resume immediately whenever cont.resume() is called on the UI thread?


Solution

  • Yes. You can study the implementation of awaitFrame in Adroind context here and use a similar technique. In particular, when coroutine is suspended with suspendCancellableCoroutine function then you can ask to resume it undispatched if you already run in the context of this dispatcher by usin resumeUndispatched function in the resulting CancellableContinuation. You have have to supply a reference to the corresponding dispatcher as a "proof" and use the following code pattern:

    with(continuation) { dispatcher.resumeUndispatched(value) }
    

    This approach is safe in the sense that if the coroutine that you are resuming is using a different dispatcher, then it will still be dispatched to the appropriate dispatcher. Dispatching is suppressed only if it is running in the dispatcher that you've specified.