如果第一次执行引发异常,则不会从指定范围执行第二个异步方法

时间:2019-09-03 11:25:09

标签: kotlin async-await coroutine

我对协程是陌生的,我想要实现的是在BaseActivity活动中包含UI,IO和DEFAULT CoroutineScope。然后将这些范围用于某些操作。

除非在suspendCoroutine {}中引发异常,否则一切似乎都可以正常工作。

如果没有异常,我可以多次使用ioScope,而不会出现任何问题。但是,如果在Response.ErrorListener中引发异常,并且我调用loginAsync(),异步将不会执行,协程将停留在userDeferred.await()。

我检查了ioScope.isActive标志。在抛出Exception之前,将标志设置为true。抛出异常后,将标记设置为false,我可以在范围内抛出异常。

我发现,当我使用ioScope.async {}函数代替context(ioScope.coroutineContext){}时,异常不会破坏范围,可以再次使用。

任何人都可以帮助我解决此问题。在文档或博客中找不到任何帮助。

BaseActivity CoroutineScopes的创建。

abstract class BaseActivity : AppCompatActivity() {
private val ioJob = Job()
private val defaultJob = Job()
private val uiJob = Job()

protected val ioScope: CoroutineScope
    get() = CoroutineScope(Dispatchers.IO + ioJob)
protected val uiScope: CoroutineScope
    get() = CoroutineScope(Dispatchers.Main + uiJob)
protected val defaultScope: CoroutineScope
    get() = CoroutineScope(Dispatchers.Default + defaultJob)


    final override fun finish() {
        super.finish()

        uiJob.cancel()
        defaultJob.cancel()
        ioJob.cancel()
        getActivityTransitions().setFinishActivityTransition(this)
    }
}

用户存储库 通过BaseActivity使用ioScope。

@Throws(LoginException::class)
suspend fun loginAsync(loginData: LoginData, context: Context): Deferred<User> {
    return ioScope.async {
        suspendCoroutine<User> { continuation ->
            val jsonObjectRequest = HttpClient.createJsonObjectRequest(
                "/users/me2",
                loginData.toJsonString(),
                Response.Listener {

                    val httpResponse : HttpResponse<User> = it.toString().jsonToObject()

                    continuation.resume(httpResponse.response)
                },
                Response.ErrorListener {
                    continuation.resumeWithException(LoginException(it))
                }
            )
            HttpClient.getInstance(context).addToRequestQueue(jsonObjectRequest)
        }
    }
}

LoginActivity

private suspend fun performLogin() {
    val loginData = LoginData(login_username_text_input.value.toString(), login_password_text_input.value.toString())

    val userDeferred = UserServerRepository(ioScope).loginAsync(loginData,this@LoginActivity);

    try {
        val result = userDeferred.await()

        login_username_text_input.value = result.company

        //HomeActivity.startActivity(this@LoginActivity)
        //finish()
    }catch (loginException: LoginException){
        login_username_text_input.value = loginException.message
    }
}

LoginActivity按钮设置

loginButton.main_button.setAsyncSafeOnClickListener(uiScope, suspend {
        performLogin()
    })

setAsyncSafeOnClickListener实现

fun View.setAsyncSafeOnClickListener(uiScope: CoroutineScope, action: suspend () -> Unit) {
    val safeClickListener = SafeClickListener {
        uiScope.launch {
            isEnabled = false
            action()
            isEnabled = true
        }
    }

    setOnClickListener(safeClickListener)
}

1 个答案:

答案 0 :(得分:1)

简短的回答:如果您要拥有强大的范围,则必须使用SupervisorJob()而不是Job()

长答案:这是一篇很棒的文章,介绍了协程作用域中的错误处理如何工作https://proandroiddev.com/kotlin-coroutine-job-hierarchy-finish-cancel-and-fail-2d3d42a768a9