10

Jetpack Compose Side-Effects II — rememberCoroutineScope

 3 years ago
source link: https://proandroiddev.com/jetpack-compose-side-effects-ii-remembercoroutinescope-76104d7ff09
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Jetpack Compose Side-Effects II — rememberCoroutineScope

In the previous part, we talked about how LaunchedEffect can be used to launch coroutines from composable and not worry about leaking the task. LaunchedEffect launches the coroutine as soon as the composition starts and is cleaned up when the composition exits automatically.

This works well for cases when you want to start a coroutine from a composable as soon as it enters composition and stops on exit, but has a few limitations which make unusable in other cases

  1. LaunchedEffect is a composable itself. This basically means it can only be started from another composable function. You cannot start LaunchedEffect from a callback (on a click of a button, for example)
  2. With LaunchedEffect, you cannot control the lifecycle of the coroutine. The coroutine starts and ends based on the Composable lifecycle and has no way to manually cancel it (in cases like cancelling an animation).

For such cases, we have rememberCoroutineScope

rememberCoroutineScope is a composable function which returns a scope. This coroutine scope is tied to the composable from where it is called and will automatically be cancelled when this composable leaves composition.

Using this scope, we can safely launch coroutines from any composable or from callbacks without worrying about the lifecycle of the coroutine.

In the example above, we get access to scope by calling rememberCoroutineScope. Since we called this function in the TimerScreen composable, the scope is tied to the lifecycle of this composable. If any coroutine is running when this composable exits composition, it will automatically be cancelled.

Now, once we have access to this scope, we can use it to launch coroutines. Here, we are launching a coroutine on click of the button which starts a timer for 5 seconds.

When a user clicks the button, the timer starts by printing “Timer started” on the console. After 5 seconds, the timer ends by printing “Timer ended”.

Now, let's say the user clicks the button and then before the timer could end, rotates the screen. This screen rotation would cause the activity to restart and composable to exit the composition. Since the scope is tied to this composable’s lifecycle, the timer would automatically cancel printing “Timer cancelled” on the console.

Manual Cancellation

Besides the composable controlling the lifecycle of the coroutine, we can manually cancel it as well. Since we have access to the scope, we can call scope.cancel() or job.cancel() to cancel coroutines manually.

Cancellation using job

To cancel coroutine, we need to save the job returned by the launch function call in a variable. Invoking job.cancel() would then cancel this specific coroutine as shown above. Here, we have a job as a state variable so that we have a reference to it even after the recomposition of the composable. We assign to it the value returned by the launch block. Now this job can be used to cancel this launch block as done in the onClick listener of Cancel button.

Now consider a case where the user clicks Start Timer button 3 times. On each click, a new coroutine would spin up starting a new timer. Since we are only storing the reference to the last job that was launched, calling job.cancel() would only cancel the most recently launched coroutine while the other two will go on to completion unless of course the entire scope is cancelled on, like in the case of screen rotation.

In the above example, to limit the number of timers to 1, we could have cancelled the already running job before starting a new one.

Cancellation using scope

In the above example, we are calling cancel on scope instead of job. This will cancel all coroutines which are running using this scope at once. One important thing to note here is that once you call cancel on the scope, it can no longer be used to launch more coroutines. For example, if you press the start timer button twice, two coroutines would spin up with two timers. Then press cancel, both the timers would stop. Post this, clicking on Start Timer button will not start any more coroutines because the scope it is attached to, has already been cancelled.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK