I want to update the UI every X seconds. I could `...
# compose
m
I want to update the UI every X seconds. I could 
launch
 a coroutine, 
delay
 it and then mutate the state. How can I correctly handle the lifecycle of the composition?
l
if you just use
launchInComposition
it will all be handled for you
m
I've read the doc for
launchInComposition
before posting here. Maybe I didn't understand how the lifecycle of a composable behaves. If I update a state, I trigger a recomposition? And the coroutine launched with
launchInComposition
will stay alive?
z
If I update a state, I trigger a recomposition?
Depends what you mean by “state”. Updating a
MutableState
value (i.e. what’s returned by
state { }
and
savedInstanceState { }
) will trigger a recomposition. This is how
collectAsState
works, for example.
And the coroutine launched with 
launchInComposition
 will stay alive?
The coroutine launched by this function will stay active as long as the
launchInComposition
remains in the composition. If you change your state in such a way that, on the next composition, you no longer call
launchInComposition
in the same position (e.g. it’s surrounded by an
if
that changes from true to false), then the coroutine will be cancelled. The function also optionally takes one or more keys, which behave like keys to
state
or
remember
– if those keys change between compositions, the coroutine will be cancelled and a new one started. More info from Adam here: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1591659323449600?thread_ts=1591653158.447300&cid=CJLTWPH7S
🙏🏼 3
m
Thank you so much ❤️ Now everything is very clear to me, reading the doc I didn't get how
launchInComposition
works
v
I’ve seen a lot of discussions around using coroutines with composables. I’m curious about whether this allows us to render views in a non-main thread? Will this ever be possible? If not, is
launchInComposition
taking care of switching threads for me?
z
The compose team has talked a lot about making it possible to execute composables on background threads. What that looks like exactly isn’t super clear, but you’ll notice that Compose actually uses a kind of “transactions” for state updates (called Frames). This is one of the reasons why side-effects must only be performed in
onCommit
-type functions (see that thread i linked above for more on this too). E.g. One of the compose devs mentioned one use case was pre-rendering
AdapterList
items in the background before they’re needed on-screen.
🙌🏼 1
launchInComposition
launches coroutines on the
Dispatchers.Main
dispatcher, and otherwise has the same semantics around threading that the standard coroutine
launch
does. If you want to switch threads inside of that you can use
withContext
or any of the other coroutine facilities for managing context.
Note that you might need to wrap updates to
MutableState.value
from different threads in
FrameManager.framed { }
still, or you’ll get an exception about not being in a frame. Apparently there will eventually be some global frame management magic that will make this unnecessary, but last time I checked it was still required.
v
Thanks Zach, this is super helpful! Glad to hear that there is less magic than what I initially thought it did! I did use
FrameManager.framed { }
recently so I exactly know which error you are talking about 😄
l
1. we are working on making composition happen on background threads. Compose was designed for this, but it has taken us longer to get there because for a long time the IR compiler didn’t support coroutines or crossinline lambdas, so we deferred the work. Now that it is here, we are patching up several places where we made single-threaded assumptions. We’ll let you know when we make real progress here. That said, launchInComposition is separate to all of this and can be used today.
2. as mentioned, the architecture of Framed (which is what MutableState uses under the hood) was designed for concurrency and is a huge piece of why concurrent composition and composition on a background thread might be possible. Also note that this whole subsystem has been undergoing some refactoring and there should soon be new support that makes it so that
FrameManager.framed { … }
won’t be needed on background threads. We are also investigating how we can make this seamlessly interleave with
launchInComposition
in pretty interesting ways, but we don’t have anything solid there yet.
v
This is really exciting progress! It has huge implications on performance and I’m really looking forward to playing with it. Thank you for keeping us in the loop like always 🙏🏼
a
yeah well, for the moment we're seeing a performance regression as a result of adding a bunch more thread safety around invalidation in preparation for this, so pardon the dust as we move in that direction 😄
😂 3
probably not noticeable for most app-level use cases just yet but it's showing up in our benchmark tracking
z
If any non-googlers on this thread are interested, it looks like “Frames” are being replaced by “Snapshots”: https://android-review.googlesource.com/c/platform/frameworks/support/+/1326201
👍 3