kotlin如何使用coroutine scope调用web服务和处理错误

How to use coroutine scope to call web service and handle error kotlin

我开发了一个 android 应用程序 kotlin.I 使用 swagger 获取我所有的 Web 服务。 我想调用 Web 服务并使用 try/catch 处理错误(如果存在)。 经过一段时间的研究,关于如何使用这个 WS,我发现我应该将协程与 Dispatchers 一起使用,我使用 GlobalScope 如下:

         GlobalScope.launch(Dispatchers.Default) {
  val productsType = mobileApi.apiMobileProductTypeGetAllPost(params, 0, 50, "", "")
withContext(Dispatchers.Main) {
    viewProductType.loadAllTypeProduct(productsType)}

这是 loadAllTypeProduct 的代码:

override fun loadAllTypeProduct(data: Array<ProductTypeData>) {
        recyclerViewProductTypeList.apply {
            recyclerViewProductTypeList.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
            recyclerViewProductTypeList.adapter = ProductTypeItemAdapter(data, this@HomeFragment, context)
        }
    }

但是,我发现我应该使用 Coroutine Scope 而不是 Global Scope 并且 Global Scope 非常令人沮丧。 因此,我将我的代码从上面的代码更改为以下代码:

   val scope = CoroutineScope(Dispatchers.Main)
        basicViewInterface.showProgressBar()
        val task = scope.launch {
            try {
               withContext(Dispatchers.IO) {
                   val productsType=  mobileApi.apiMobileProductTypeGetAllPost(params, 0, 50, "", "")
                   viewProductType.loadAllTypeProduct(productsType)
                }
                basicViewInterface.hideProgressBar()
            } catch (e: Throwable) {
                e.printStackTrace()
            }
        }
        if (task.isCancelled){
            basicViewInterface.displayError("error")
        }

我想调用我的 WS 并使用 try/catch 处理异常并在 Toast 中显示错误,我该怎么做。

如果您想自己处理异常,则需要使用 CoroutineExceptionHandler

Documentation states that:

An optional element in the coroutine context to handle uncaught exceptions.

Normally, uncaught exceptions can only result from coroutines created using the launch builder. A coroutine that was created using async always catches all its exceptions and represents them in the resulting Deferred object.

怎么样?创建此异常处理程序的对象,然后将其作为上下文传递给您的协程,如下所示:

// Create object of exception handler
val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
    // Here you can handle your exception
}

val scope = CoroutineScope(Dispatchers.Main)
basicViewInterface.showProgressBar()
// pass exception handler as context to launch method
val task = scope.launch(exceptionHandler) {
        withContext(Dispatchers.IO) {
            val productsType=  mobileApi.apiMobileProductTypeGetAllPost(params, 0, 50, "", "")
            viewProductType.loadAllTypeProduct(productsType)
        }
        basicViewInterface.hideProgressBar()
    }

协程作用域的要点在于,它在您检测到用户已导航离开当前 activity 的地方可用,因此您可以在一个中心位置取消它并拥有所有子协程被取消。您的更改只是创建了一个本地的一次性范围,与使用 GlobalScope 一样糟糕。如果您的代码在 Activity 或 Fragment 中,则使 class 实现 CoroutineScope by MainScope,您可以在 CoroutineScopedocumentation 中看到更详细的示例.

至于异常处理,代码应该和你发的差不多,只是注意切换上下文。协程范围应指定 Main 作为调度程序,您应该仅针对阻塞网络调用切换到 IO,如下所示:

basicViewInterface.showProgressBar()
scope.launch {
    try {
        val productsType = withContext(Dispatchers.IO) {
            mobileApi.apiMobileProductTypeGetAllPost(params, 0, 50, "", "")
        }
        viewProductType.loadAllTypeProduct(productsType)
        basicViewInterface.hideProgressBar()
    } catch (e: Throwable) {
        basicViewInterface.displayError("error")
    }
}

如果将 withContext 推入 apiMobileProductTypeGetAllPost 的正文中,它会更好、更干净,因为这是一个本地化的问题。从外部看,它应该只是另一个可挂起的函数,您可以调用它而不用担心给定的实现是否在非阻塞上阻塞等低级细节。

我注意到其他人提到协程异常处理程序,但我不建议使用它。它仅在协程层次结构的顶层工作,其目的是仅捕获由于编程错误而未在业务代码中适当处理的那些异常。它相当于 Java 中的 Thread.uncaughtExceptionHandler