Home > other >  LaunchedEffect() cannot be relaunched
LaunchedEffect() cannot be relaunched

Time:05-09

Read this LaunchedEffect take a variable number of keys as a parameter that are used to restart the effect whenever one of those keys changes. from enter image description here

My Android Studio:

Android Studio Chipmunk | 2021.2.1 Beta 4
Build #AI-212.5712.43.2112.8233820, built on March 1, 2022
Runtime version: 11.0.12 0-b1504.28-7817840 x86_64

My compose version is 1.1.1


Update 2: I accept the answer from @MARSK. Just want to put more info at here for other buddy's information.

I was following the codepath this this codelab. So the code above was a part of it, the key point here I think is:

  1. The key should be defined as a State, like remember { mutableStateOf(1) }
  2. The expected result should be an infinite loop, because inside of the LaunchedEffect block, the statement key = 2 will always trigger a recomposition.

Why I wasn't able to figuring out how it works, and I got only 1 log print? That was because of the onTimeout callback, from parent level of LandingScreen composable, the function will remove this(LandingScreen) composable completely from the tree, thus it won't have any chance to be re-composed.

CodePudding user response:

var key by remember { mutableStateOf (1) }

The above is crucial for it to work. The code is working as expected. Here's the details.

When you run this simple code block

var key by remember { mutableStateOf(1) }
LaunchedEffect(key) {    
  Log.e("xx", "Running key $key")
  delay(2000)
  key = 2
}

key = 3

The code runs as: key is initialized to 1, and the effect runs for the value of key = 1, but, as correctly (I hope) pointed out by the OP in the comment below, LaunchedEffect takes a bit of a time to spawn a coroutine, which is enough for the control to shift to the next statement and alter the value of the key to be 3. This ensures that the value of key as 1 is never logged.

After this and a delay of two seconds, the value of the key changes to one, triggering an entire recomposition of the Composable, which means that the entire composable shall re-execute. Here's how that goes:

The key remains as 2, because we're using remember LaunchedEffect gets called with a value of 2, but again, due to the coroutine-spawning time, the key is modified to be 3, WHICH, being different than 2, RE-TRIGGERS a recomposition, hence, never logging the 2, but only logging the 3, since that is what the value of key is now. Again, the previous LaunchedEffect is not cancelled, and when its two-second delay is over, it will switch the value to 2 again, which immediately shifts back to 3 because of this composition. Hence, you'll get logs of Running 3 consistently, with two logs occurring very close together in time, while the next on will be separated by a two-second interval.

This is what you get

17:28:43.241 : Running key 3
17:28:43.269 : Running key 3
17:28:45.298 : Running key 3
17:28:45.318 : Running key 3
17:28:47.356 : Running key 3
17:28:47.374 : Running key 3
17:28:49.422 : Running key 3
17:28:49.443 : Running key 3
17:28:51.501 : Running key 3
17:28:51.534 : Running key 3
17:28:53.591 : Running key 3
17:28:53.607 : Running key 3
17:28:55.649 : Running key 3
17:28:55.689 : Running key 3

Now, if you wish to correctly log the value with which the effect was called, you need to store it at the entry point itself. Like so,

var key by remember { mutableStateOf(1) }
var keyStore: Int
LaunchedEffect(key.also { keyStore = key }) {
    "Running key $keyStore".log()
    delay(2000)
    key = 2
}
key = 3

This produces the correct runtime logs

17:34:13.790 : Running key 1
17:34:13.821 : Running key 3
17:34:15.847 : Running key 2
17:34:15.862 : Running key 3
17:34:17.903 : Running key 2
17:34:17.922 : Running key 3
17:34:19.982 : Running key 2
17:34:20.015 : Running key 3
17:34:22.055 : Running key 2
17:34:22.073 : Running key 3
17:34:24.135 : Running key 2
17:34:24.154 : Running key 3
  • Related