I don't understand why the following two methods give different results:
@Composable
fun MainScreen2() {
var counter by remember { mutableIntStateOf(0) }
LaunchedEffect(key1 = Unit, block = {
delay(5000)
Log.i("Launcher1", "1: $counter")
})
Column {
Log.i("Launcher", "column compose")
Text(text = "Count: $counter")
Button(onClick = { counter++ }) {
Text(text = "ADD")
}
test2(count = counter)
}
}
@Composable
fun test2(count: Int) {
LaunchedEffect(key1 = true, block = {
delay(5000)
Log.i("Launcher", "2: $count")
})
}
When I click the "ADD" button three times within one seconds, after 5 seconds Log 1 prints: 1: 3
and Log 2 prints 2: 0
.
When MainScreen2
enters the composition the LaunchedEffect
is executed. The block you provide captures all objects that are used in that block in a closure. It's like a static snapshot that won't be affected by changes that will happen while the block executes. In this case the only object captured is counter
. When 5 seconds have passed it will print its content to the log.
Now, by this reasoning it actuallly should always print 0: After all, counter
is captured right on the first composition, before any buttons were pressed. And at that time it is always 0.
What's going on here is that counter
isn't actually an Int
, it is a delegate to a MutableState that contains an Int. You created that delegate by using the by
keyword in the declaration. What this means is that, although it looks like an Int, it is actually the State object itself, only that everytime you access the "Int", the getValue
function is called on the State when you want to read it, and the setValue
function is called on write access. Delegates are just synatax sugar provided by Kotlin to make your code look nicer.
The LaunchedEffect
actually captures the MutableState in its closure, and since you remember
ed that MutableState, it will always be the same on all recompositions. So when you click the button, the same MutableState that the LaunchedEffect captured is modified, so when 5 seconds have passed, the LaunchedEffect just reads the current value from that state and prints it.
That's the reason why this even works in the first place.
But that also explains why your second LaunchedEffect
only prints 0: It doesn't capture a delegate to a MutableState, it captures the parameter count
. And that is really and Int, not just a delegate to a MutableState that contains an Int. Since the second LaunchedEffect is never recomposed because you provided true
as its key, it will only ever have the count
parameter in its closure from when test2
entered the composition. At that time it was 0, so 0 is printed.
If you would provide the second LaunchedEffect
with count
as its key, then every time that changes the block of the LaunchedEffect would be restarted and the then current count
would be printed to the log. The 5 second delay will be also executed again, so you have to wait for 5 seconds after each change of count
to see the log entry.