https://kotlinlang.org logo
#coroutines
Title
# coroutines
a

Alejo

06/27/2021, 2:23 PM
Hi everyone!  I want to update my app behavior depending on if a user is touching the screen or not. So I came with the idea of having a MutableSharedFlow that emits user interaction and collecting the values with collectLatest. The problem is that I'm only interested in the last event and would want to drop all older processing. How could I achieve this? The code looks like this:
Copy code
val userInteracting = MutableSharedFlow<Unit>(replay = 0)

onEvent(){
    coroutineScope.launch {
        userInteracting.emit(Unit)
    }
}

coroutineScope.launch {
    userInteracting.collectLatest {
        showSky.value = false
        delay(5000)
        showSky.value = true
    }
}
m

mbonnin

06/27/2021, 2:29 PM
I guess you could also do something like
Copy code
MutableSharedFlow(
        replay = 0,
        extraBufferCapacity = 1,
        onBufferOverflow = BufferOverflow.DROP_OLDEST
    )
a

Alejo

06/27/2021, 2:32 PM
Thanks Martin! I will try this
Nop, it's not working. Sadly it keeps processing older events 😞
m

mbonnin

06/27/2021, 2:46 PM
What do you mean by "keep processing older events" ?
If your consumer consumes items faster than your producer produces them, all events will be processed
What you could do is emit a boolean whether the screen is touched and only do your processing if this boolean is true?
a

Alejo

06/27/2021, 2:58 PM
I had a delay(5000) because my main goal is to update the UI status if the user stops interacting with the app for 5 seconds. With this implementation even if I keep touching the screen and generating new events, the delay for an older event completes and updates the status, though. Thats what I mean by "keep processing older events"
m

mbonnin

06/27/2021, 3:07 PM
That's unexpected...
a

Alejo

06/27/2021, 3:15 PM
Thanks a lot for your help anyway
m

mbonnin

06/27/2021, 3:16 PM
Yea sorry, I'd expect the
collectLatest
to cancel the previous actions...
Maybe emit a timestamp instead of Unit ? So you can debug which event is 100% completed vs the ones that get cancelled?
a

Alejo

06/27/2021, 3:21 PM
Yeah, I will try this and get more insights on whats happening
c

CLOVIS

06/27/2021, 3:59 PM
Create a Job for your animation, whenever the user touches the screen cancel any previous job (so whatever animation currently in
delay
will die)
a

Alejo

06/27/2021, 4:39 PM
Trying @mbonnin suggestion on emitting a timestamp instead of Unit I realized that the first time is working as expected, but the second time I start emitting events it processes the first one and the last one as you can see in these logs.
Copy code
First attemp 
2021-06-27 13:27:25.901 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811245901 received
2021-06-27 13:27:26.477 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811246477 received
2021-06-27 13:27:27.011 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811247010 received
2021-06-27 13:27:27.539 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811247538 received
2021-06-27 13:27:28.072 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811248071 received
2021-06-27 13:27:28.596 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811248595 received
2021-06-27 13:27:29.090 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811249089 received
2021-06-27 13:27:29.603 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811249603 received
2021-06-27 13:27:30.133 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811250133 received
2021-06-27 13:27:30.645 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811250644 received
2021-06-27 13:27:31.149 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811251148 received
2021-06-27 13:27:31.674 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811251673 received
2021-06-27 13:27:32.197 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811252196 received
2021-06-27 13:27:32.736 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811252736 received
2021-06-27 13:27:33.272 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811253271 received
2021-06-27 13:27:33.803 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811253803 received
2021-06-27 13:27:34.350 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811254349 received
2021-06-27 13:27:34.946 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811254946 received
2021-06-27 13:27:39.947 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811254946 processed

Second attemp
2021-06-27 13:29:06.886 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811346886 received
2021-06-27 13:29:07.368 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811347367 received
2021-06-27 13:29:07.785 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811347784 received
2021-06-27 13:29:08.181 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811348181 received
2021-06-27 13:29:08.647 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811348646 received
2021-06-27 13:29:09.095 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811349095 received
2021-06-27 13:29:09.537 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811349536 received
2021-06-27 13:29:09.913 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811349912 received
2021-06-27 13:29:10.308 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811350307 received
2021-06-27 13:29:10.691 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811350690 received
2021-06-27 13:29:11.887 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811346886 processed
2021-06-27 13:29:15.692 1308-1308/com.example.androiddevchallenge.mock D/pointerInteropFilter: 1624811350690 processed
Thanks for your suggestion @CLOVIS. Did you mean something like this:
Copy code
var job:Job? = null
    coroutineScope.launch {
        userInteracting.collectLatest {
            job?.cancel()
            job = launch {
                Log.d("pointerInteropFilter","$it received")
                showSky.value = false
                delay(5000)
                showSky.value = true
                Log.d("pointerInteropFilter","$it processed")
            }
        }
    }
c

CLOVIS

06/27/2021, 4:43 PM
Yes, for example. Note though that I have no idea what the performance cost to cancelling a Job is. I think it's pretty much free, but that might require a benchmark.
I think that's the easiest way, but I'm not an expert so...
m

mbonnin

06/27/2021, 4:51 PM
I would add a
asSharedFlow
:
Copy code
userInteracting.asSharedFlow().collectLatest {
}
Not 100% sure what the behaviour of collecting the
MutableSharedFlow
twice is (not sure it's even allowed.. looks like it's allowed)
In all cases, if it works once but not twice, looks like there's something to investigate there. Maybe you still have the old collecting in the background or so
c

CLOVIS

06/27/2021, 5:38 PM
My understanding is that the definition of SharedFlow is that it can be collected multiple times 🤔
1
m

mbonnin

06/27/2021, 5:41 PM
Yep, just I always use the
asSharedFlow
to expose a read-only version but looks like collecting the
MutableSharedFlow
is the same
a

Alejo

06/27/2021, 5:52 PM
With the job variant I had the same results but a little slower, so I will rollback to the first version and ask in the compose channel, because maybe is something wrong with my composable that is causing this issue. I'm pretty sure is the composable. Thanks both for your help I will keep you posted on this issue.
👍 1
Just in case someone faces the same issue in the future, my problem was not remembering the MutableSharedFlow in Compose, and that cause the strange behavior. Lesson learned, remember to "remember"
🙏 1
👍 2
4 Views