Meta
Tenho uma função de computação pesada calculateItems: suspend (String) -> Sequence<String>
.
Quero carregar as saídas desse cálculo enquanto mostro o progresso ao usuário e mantenho a UI responsiva. Também quero cancelar esse cálculo caso a entrada do usuário mude e começar de novo quando o usuário clicar em um botão.
Abordagem
Eu uso produceState
. Dentro de produceState
, delego essa computação a um não-Main-dispatcher para manter a UI responsiva. Eu também coleto os itens emitidos dessa sequência e atualizo o progresso em cada item recebido.
val totalSize = 255 // for sake of this example
var input by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue("Test Input"))
}
var doCalculation by rememberSaveable(input) {
mutableStateOf(false)
}
val resultState by produceState(
State.Null as State,
input,
doCalculation,
) {
value = DealingState.Null
if (!doCalculation) {
value = DealingState.Null
return@produceState
}
value = DealingState.Loading(
progress = 0,
required = totalSize,
)
launch(Dispatchers.Default) {
runCatching {
val resultList = mutableListOf<String>()
calculateItems(input).forEach {
resultList.add(it)
value = State.Loading(
progress = resultList.size,
required = totalSize,
)
}
value = State.Success(resultList)
}.getOrElse {
value = State.Failure(it)
}
}
}
O que eu tentei
Eu tentei as seguintes coisas:
- defina um escopo de corrotina específico para esta rotina e chame
scope.cancel()
quando um novo estado deve ser produzido.
val scope = CoroutineScope(Dispatchers.Default)
val resultState by produceState(...) {
scope.cancel()
....
scope.launch(Dispatchers.Default) {...}
}
- armazenar o trabalho
launch
e cancelar o trabalho ou os filhos
var job: Job? = null
val resultState by produceState(...) {
job?.cancel() // and job?.cancelChildren()
....
job = launch(Dispatchers.Default) {...}
}