简体   繁体   English

kotlin如何使用协程作用域调用web服务并处理错误

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

I develop an android app with kotlin.I use swagger to get all my Web Services.我使用 kotlin 开发了一个 android 应用程序。我使用 swagger 来获取我所有的 Web 服务。 I want to call the web service and handle the error with try/catch if exist.我想调用 Web 服务并使用 try/catch(如果存在)处理错误。 After a while of research, about How to consume this WS, I found that I should use coroutine with Dispatchers, I use GlobalScope as the following :经过一段时间的研究,关于如何使用这个 WS,我发现我应该使用协程和 Dispatchers,我使用 GlobalScope 如下:

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

Here's the code of loadAllTypeProduct :这是 loadAllTypeProduct 的代码:

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

But,I found that I should use Coroutine Scope instead of Global Scope and the Global Scope is highly disouraged.但是,我发现我应该使用 Coroutine Scope 而不是 Global Scope,并且 Global Scope 非常不鼓励。 So, I change my code from code above to the following one :因此,我将代码从上面的代码更改为以下代码:

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

I want to call my WS and handle the exception with try/catch and dispaly the error in a Toast,How can I do that.我想调用我的 WS 并使用 try/catch 处理异常并在 Toast 中显示错误,我该怎么做。

The point of the coroutine scope is that it's available at the place where you detect that the user has navigated away from the current activity, so that you may cancel it at one central place and have all the child coroutines cancelled.协程作用域的重点在于它在您检测到用户已离开当前活动的地方可用,因此您可以在一个中心位置取消它并取消所有子协程。 Your change, which just creates a local, throwaway scope, is just as bad as using GlobalScope .您的更改只会创建一个本地的、一次性的范围,与使用GlobalScope一样糟糕。 If your code is within an Activity or a Fragment, then make that class implement CoroutineScope by MainScope , you can see a more detailed example in the documentation of CoroutineScope .如果您的代码在 Activity 或 Fragment 中,则CoroutineScope by MainScope使该类实现CoroutineScope by MainScope ,您可以在CoroutineScope文档中看到更详细的示例。

As for handling exceptions, the code should look pretty much as you posted it, just pay attention to switching contexts.至于处理异常,代码应该看起来和你发布的差不多,只需要注意切换上下文。 The coroutine scope should specify Main as the dispatcher and you should switch to IO only for the blocking network call, like this:协程范围应该将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")
    }
}

It would be even better and cleaner if you pushed that withContext into the body of apiMobileProductTypeGetAllPost because it's a concern localized to it.如果你推,这将是更好的和更清洁的withContext进入体内apiMobileProductTypeGetAllPost因为它定位于它的担忧。 From the outside it should be just another suspendable function you can call without worrying about such low-level details as whether the given implementation is blocking on non-blocking.从外部看,它应该只是另一个可挂起的函数,您可以调用而不必担心给定的实现是否在非阻塞时阻塞等低级细节。

I noticed others mentioning the coroutine exception handler, but I don't recommend using it.我注意到其他人提到了协程异常处理程序,但我不建议使用它。 It works only at the top level of the coroutine hierarchy and its purpose is to only catch those exceptions that were not appropriately handled within the business code, due to a programming error.它仅在协程层次结构的顶层工作,其目的是仅捕获那些由于编程错误而未在业务代码中正确处理的异常。 It is the equivalent of Thread.uncaughtExceptionHandler in Java.它相当于 Java 中的Thread.uncaughtExceptionHandler

If you want to handle exceptions on your own then you'll need to use CoroutineExceptionHandler .如果你想自己处理异常,那么你需要使用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.使用 async 创建的协程始终捕获其所有异常并在生成的 Deferred 对象中表示它们。

How?如何? Create object of this exception handler and then pass it to your coroutine as context like below:创建此异常处理程序的对象,然后将其作为上下文传递给您的协程,如下所示:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM