Kotlinx.coroutines: Make Android UI dispatcher asynchronous by default

Created on 8 Jul 2018  Â·  12Comments  Â·  Source: Kotlin/kotlinx.coroutines

See discussion in #381

We can make UI asynchronous by default and provide a fallback (system property?) for sync behaviour and something like SyncUI.

Asynchronous handlers are the new feature in Android and it's unclear for us (as we're not Android devs) whether it's sane default which is safe to expose, so we'd like to hear community opinion on this.

help wanted

Most helpful comment

I'm doing it. I spent 2m making the change and am entering hour 2 writing
tests.

On Mon, Sep 17, 2018 at 2:03 PM Louis CAD notifications@github.com wrote:

Can't agree more.
Did anyone started a PR? If not, I'll be happy to take it as my first non
typo fix PR for this repo.

On Mon, Sep 17, 2018, 4:53 PM Adam Powell notifications@github.com
wrote:

Giving this a bump now that UI has become Dispatchers.Main. Now that this
is generalized beyond UI into all other main looper tasks it would be
great
to get this default behavior change in for 1.0 so that coroutines-based
code doesn't start relying on the compatibility vsync barrier behavior
out
of the gate.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<
https://github.com/Kotlin/kotlinx.coroutines/issues/427#issuecomment-422047742
,
or mute the thread
<
https://github.com/notifications/unsubscribe-auth/AGpvBVw1rG9H_EUSXwBfQMjgsEulxwVZks5ub7eBgaJpZM4VGu7I

.

—
You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/Kotlin/kotlinx.coroutines/issues/427#issuecomment-422113536,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAEEEQqEIdewSHxEG6NoiVAB5r6XfsT2ks5ub-P6gaJpZM4VGu7I
.

All 12 comments

cc @JakeWharton

I personally never rely on dispatch and waiting layout passes or frames in coroutines. I just rely on which thread/thread pool they will run.
One exception is a place where I use UI.awaitFrame() for imperative animation in a for loop.

If we want to be super precise there are two axes at play here: UI vs. Main thread and sync vs. async. I'm not sure it's worth slicing all of them into official API since it'll only lead to, "which should I use?" questions where the answer will almost always be, "async/main thread."

We do make distinctions in Android APIs around main vs. UI thread - they aren't the same since you can create a window with UI thread that is a looper thread other than the app's main ActivityThread. This is uncommon and usually only comes up in system code, but we do try to keep API docs and thread hint annotations precise around this.

Then you have the sync/async distinction here. Ever since Jellybean introduced vsync barriers that delay posted messages until after a pending measure/layout/draw operation when the looper thread is otherwise idle there have been APIs to bypass it and get lower latency behavior if you don't need the results of the pending view tree traversal. The functionality itself is not a new feature; the only new feature is the Handler.createAsync API that does some boilerplate work for you. We plan to have a backport available for this API soonish. (Just a matter of library release schedules.)

Given that:

  • You can easily create a coroutine context from any Handler
  • If you're being precise the only way to correctly reference a UI thread is from an attached View's getHandler method in that specific window, which isn't particularly practical for many use cases
  • Nearly all app UIs use the main thread as their UI thread
  • Nearly all code that isn't part of a custom View implementation doesn't need the vsync barrier for correctness

The only looper thread that you can offer a global coroutine context for is the app's main thread, and offering two options out of the box probably isn't worth the, "which should I use?" speed bump. The async variant is almost certainly what people want outside of some special cases.

The UI dispatcher is kind of unfortunately named; Main or MainThread would be more accurate and consistent with Android documentation/API.

Regarding UI.awaitFrame(), with its current implementation it suspends until the frame callback, which occurs immediately _before_ the next measure/layout/draw pass - unless it hasn't cached the Choreographer reference yet, in which case the handler post it uses to fetch that reference will incur the 1-frame latency that async handlers would otherwise skip.

In other words, awaitFrame's correctness wouldn't be altered by the proposed change, but the first use would save a frame.

awaitFrame is also a bit of a foot gun in concept; a frame callback happens at a performance-critical time so any expensive operation performed there before another suspend could lead to a delayed or dropped frame. Using it for imperative animations is also dangerous, as you still need to cancel/clean up the handler loop that it causes when the associated view/window is no longer visible. We've had quite a few bug reports from apps that have their process stuck in one of these message loops draining battery when the app isn't even visible. Giving it such high visibility on HandlerContext could lead to more of these kinds of bugs.

Could be nice to have a prototype of such a CoroutineDispatcher so we can try in our projects and see if there's any impact on stability, and see how the performances are different.

Using the snippet in #381 for compatibility, createAsyncHandler(Looper.getMainLooper()).asCoroutineDispatcher() should be sufficient for prototyping.

I'd even link to the comment containing the snippet here.

I'll try, thanks!

this article may be of interest https://medium.com/@sweers/rxandroids-new-async-api-4ab5b3ad3e93

Giving this a bump now that UI has become Dispatchers.Main. Now that this is generalized beyond UI into all other main looper tasks it would be great to get this default behavior change in for 1.0 so that coroutines-based code doesn't start relying on the compatibility vsync barrier behavior out of the gate.

Can't agree more.
Did anyone started a PR? If not, I'll be happy to take it as my first non
typo fix PR for this repo.

On Mon, Sep 17, 2018, 4:53 PM Adam Powell notifications@github.com wrote:

Giving this a bump now that UI has become Dispatchers.Main. Now that this
is generalized beyond UI into all other main looper tasks it would be great
to get this default behavior change in for 1.0 so that coroutines-based
code doesn't start relying on the compatibility vsync barrier behavior out
of the gate.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/Kotlin/kotlinx.coroutines/issues/427#issuecomment-422047742,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AGpvBVw1rG9H_EUSXwBfQMjgsEulxwVZks5ub7eBgaJpZM4VGu7I
.

I'm doing it. I spent 2m making the change and am entering hour 2 writing
tests.

On Mon, Sep 17, 2018 at 2:03 PM Louis CAD notifications@github.com wrote:

Can't agree more.
Did anyone started a PR? If not, I'll be happy to take it as my first non
typo fix PR for this repo.

On Mon, Sep 17, 2018, 4:53 PM Adam Powell notifications@github.com
wrote:

Giving this a bump now that UI has become Dispatchers.Main. Now that this
is generalized beyond UI into all other main looper tasks it would be
great
to get this default behavior change in for 1.0 so that coroutines-based
code doesn't start relying on the compatibility vsync barrier behavior
out
of the gate.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<
https://github.com/Kotlin/kotlinx.coroutines/issues/427#issuecomment-422047742
,
or mute the thread
<
https://github.com/notifications/unsubscribe-auth/AGpvBVw1rG9H_EUSXwBfQMjgsEulxwVZks5ub7eBgaJpZM4VGu7I

.

—
You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/Kotlin/kotlinx.coroutines/issues/427#issuecomment-422113536,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAEEEQqEIdewSHxEG6NoiVAB5r6XfsT2ks5ub-P6gaJpZM4VGu7I
.

This can be closed now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sky87 picture sky87  Â·  3Comments

iTanChi picture iTanChi  Â·  3Comments

petersamokhin picture petersamokhin  Â·  3Comments

Pitel picture Pitel  Â·  3Comments

mhernand40 picture mhernand40  Â·  3Comments