【问题标题】:Second async method is not executed from specified scope if first execution threw exception如果第一次执行引发异常,则不会从指定范围执行第二个异步方法
【发布时间】:2019-09-03 11:25:09
【问题描述】:

我想要实现的是在我的 BaseActivity 活动中拥有 UI、IO 和 DEFAULT CoroutineScope。然后将这些作用域用于一些操作。

除非在suspendCoroutine {}中抛出异常,否则一切似乎都正常。

如果没有抛出异常,我可以多次使用 ioScope 而不会出现问题。但是如果在 Response.ErrorListener 中抛出异常并且我调用 loginAsync(),async 将不会被执行,协程会卡在 userDeferred.await()。

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

我发现当我使用 withContext(ioScope.coroutineContext){} 代替 ioScope.async{ } 函数时,异常不会破坏作用域并且可以再次使用。

谁能帮我解决这个问题。我在文档和博客中都找不到任何帮助。

基础活动 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)
        }
    }
}

登录活动

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)
}

【问题讨论】:

    标签: kotlin async-await coroutine


    【解决方案1】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多