Lilly
07/29/2022, 6:18 AMfun someViewModelCall() {
viewModelScope.launch(Dispatchers.Default) { // light stuff }
}
EDIT: Alternate question would be, if it is common that skipped frames happen ocasionally?Arkadii Ivanov
07/29/2022, 6:22 AMLilly
07/29/2022, 6:24 AMArkadii Ivanov
07/29/2022, 6:34 AMColton Idle
07/29/2022, 7:29 AMsigningConfig =
if (properties.containsKey("android.injected.invoked.from.ide")) {
signingConfigs.getByName("debug")
} else {
signingConfigs.getByName("release")
}
Lilly
07/29/2022, 7:50 AMAdam Powell
07/29/2022, 1:04 PMviewModelScope.launch
in the implementation details of a ViewModel method. You're now responsible for making sure you don't have concurrent operations disrupting one another and for managing cancellation in the event you no longer want the operation to proceed before the ViewModel is clearedLilly
07/29/2022, 4:29 PMIn terms of code patterns, the snippet in the OP can get risky because you've now introduced a fire and forget operation by performing aCan you please elaborate on this please. I mean usingin the implementation details of a ViewModel method.viewModelScope.launch
viewModelScope.launch
is the only way to start a coroutine and I thought viewModel is the right place to start coroutines?fun someViewModelCall() {
viewModelScope.launch(Dispatchers.Default) { // light stuff }
}
becomes
suspend fun someViewModelCall() {
// light or heavy operation
}
Arkadii Ivanov
07/29/2022, 4:42 PMLilly
07/29/2022, 4:48 PMArkadii Ivanov
07/29/2022, 4:49 PMAdam Powell
07/29/2022, 5:10 PMColton Idle
07/29/2022, 5:11 PMAdam Powell
07/29/2022, 5:11 PMval myViewModelScope = CoroutineScope(myFavoriteDispatcher + whateverElse + Job())
and then doing myViewModelScope.cancel()
in onCleared
is trivially easy but it makes you stop and think about the behaviors at play, and it doesn't create a gravity well around, "well, viewModelScope
is already here so that must be what I want, right?"Colton Idle
07/29/2022, 5:15 PMAdam Powell
07/29/2022, 5:15 PMsuspend fun
on your viewmodel then the viewmodel can do things like suspend fun doThing(...) = viewModelScope.async { doRealWorkForThing(...) }.await()
which means the operation can still keep going, you can store the returned Deferred
or Job
in the viewmodel to dedupe the operation if multiple calls come in, but you still have the signal available on whether a caller is still listeningFlow.stateIn
operator does by accepting a scope to collect in on behalf of multiple collectors that might come or go at any timeval someFlow = blah.stuff().operators().stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue)
is so useful in viewmodels tooWhileSubscribed
defines that, "does anyone still care?" aspect that can shut the upstream operations down when the answer is, "no"fun doThing(...) { viewModelScope.launch { ... } }
then you've thrown away any possible, "is anyone still listening for the results of what I'm doing here?" signalLilly
07/30/2022, 11:18 PM1:
val scope = rememberCoroutineScope()
DisposableEffect(Unit) {
scope.launch { viewModel/presenter.callSuspendFun() }
onDispose {}
}
2:
LaunchedEffect() {
launch { viewModel/presenter.callSuspendFun() }
}
Adam Powell
08/02/2022, 2:54 PMViewModelStoreOwner
and it performs some handoff of currently live viewmodels across android activity recreations. They aren't singletons but they outlive individual Activity
instances across configuration changeslaunch
inside the LaunchedEffect
block any more than you need to write things like
myScope.launch {
launch { callSuspendFun() }
}
Lilly
08/03/2022, 6:10 PMLaunchedEffect(connectionState) {
Log.w("ParameterScreen", "LaunchedEffect, conState $connectionState.")
if (connectionState == Protocol.State.READY) {
Log.e("ParameterScreen", "collecting.")
parameterPresenter.loadState()
}
}
It does not happen often and so I have no logs for the moment. But constate
is 2 times the same and still loadState()
is called. Or is this behavior intended?Adam Powell
08/03/2022, 7:03 PMval token = remember { Any() }
LaunchedEffect(connectionState) {
Log.w("ParameterScreen", "LaunchedEffect, conState $connectionState, token $token")
token
there when you observe the otherwise same log message printed more than once, then you have different instances of the composableLilly
08/03/2022, 8:48 PMW/ParameterScreen: LaunchedEffect, conState READY, hash: 229053043, token: java.lang.Object@e640d2b.
E/ParameterScreen: collecting.
E/BasePresenter: isLoading true
E/ParameterPresenter: fetch web-keys.
+ 1 second later
W/ParameterScreen: LaunchedEffect, conState READY, hash: 229053043, token: java.lang.Object@e640d2b.
E/ParameterScreen: collecting.
E/BasePresenter: isLoading true
E/ParameterPresenter: fetch web-keys.
And a bit later the coroutine is canceled:
W/System.err: kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@55683c8
To enusre that connectionState
is in both cases the same I also logged its hashCode, represented as hash
in the logs. I'm using compose version 1.2.0. Any ideas? Just for completeness...this didn't happen once with DisposableEffectAdam Powell
08/03/2022, 9:17 PM