mingkangpan
03/06/2020, 10:25 AMsupervisorScope {
async {
throw Exception()
}
async {
throw Exception()
}
}
^ app doesn't crash
supervisorScope {
launch {
throw Exception()
}
launch {
throw Exception()
}
}
^ app crashes
what am I doing wrong?Manuel Vivo
03/06/2020, 10:36 AMasync
has a SupervisorJob
as a parent (which is in that case), it won’t crash until you call .await()
. launch
automatically throws the exceptions as soon as it happens but that’s not the case of async
. It’ll hold it until you call await
Manuel Vivo
03/06/2020, 10:37 AMsupervisorScope
for coroutineScope
, now the parent Job
of async is of type Job
(not SupervisorJob
) and the exception will be propagated as soon as it happensManuel Vivo
03/06/2020, 10:38 AMSupervisorJob
let’s the coroutine handle the exception (and crash if it hasn’t been handled)Manuel Vivo
03/06/2020, 10:39 AMmingkangpan
03/06/2020, 10:46 AMoverride fun onChanged(t: AccountInfo) {
fetchDataAfterAccountInfo?.cancel()
fetchDataAfterAccountInfo = viewModelScope.launch {
try {
supervisorScope {
launch {
val purchases = appService.fetchPurchasesWithPickupData().purchases
pickPurchasesLiveData.postValue(purchases)
}
launch {
val paymentPlan = appService.fetchPaymentPlan()
paymentPlanLiveData.postValue(paymentPlan.instalments)
}
}
} catch (e : Exception) {
Timber.e(e, "loading data after accountInfo failed")
}
}
}
Manuel Vivo
03/06/2020, 10:47 AMsupervisorScope
is not going to propagate the exception up to the try/catch, if you want that behavior, use coroutineScope
mingkangpan
03/06/2020, 10:48 AMmingkangpan
03/06/2020, 10:48 AMManuel Vivo
03/06/2020, 10:50 AMsupervisorScope
but try/catch the inner launches instead of try/catching the supervisorScope
callmingkangpan
03/06/2020, 11:00 AMoverride fun onChanged(info: AccountInfo) {
fetchDataAfterAccountInfo?.cancel()
fetchDataAfterAccountInfo = viewModelScope.launch(CoroutineExceptionHandler { _, e -> Timber.e(e, "loading data after accountInfo failed") }) {
supervisorScope {
launch {
val purchases = appService.fetchPurchasesWithPickupData().purchases
pickPurchasesLiveData.postValue(purchases)
}
launch {
val paymentPlan = appService.fetchPaymentPlan()
paymentPlanLiveData.postValue(paymentPlan.instalments)
}
}
}
}
mingkangpan
03/06/2020, 11:00 AMManuel Vivo
03/12/2020, 7:08 AMManuel Vivo
03/12/2020, 9:09 AMManuel Vivo
03/12/2020, 9:09 AMmingkangpan
03/12/2020, 9:22 AMEvan R.
03/12/2020, 12:38 PMjob
as CoroutineScope(SupervisorJob())
and then perform multiple launch {}
invocations off of that so they can still run independently.Evan R.
03/12/2020, 12:38 PM