使用Kotlin协程将异常从后台线程传播到主线程

cigdeys3  于 2023-05-18  发布在  Kotlin
关注(0)|答案(1)|浏览(194)

使用Kotlin协程,我需要在一个try块中同时并行运行两个I/O工作,而不会相互阻塞。
如果任何工作失败,那么应该在单个catch块中捕获它。
两个操作是:
a)以10秒的间隔连续ping服务器。
B)使用okhttp进行网络呼叫。
我需要在suspend函数中介绍这两个操作。PSB代码:

suspend fun sendAllRequests() {
    try {
      pingServer()
    
      makeNetworkCall()
    
    } catch(exception: CustomException) {
      // Do some other work and throw exception to UI
      throw RuntimeException("Custom Error")    
    }
}
suspend fun pingServer() {
      job = GlobalScope.launch {
      while(true) {
         delay(10000) {
         if(isPingSuccess) {
           Log("Connection active")
         } else {
           Log("Connection Inactive")
           job.cancel()
           throw RuntimeException("Ping failed")
        }
      } 
    }
  }
suspend fun makeNetworkCall() {
    //This is a pseudo code just for explanation
    okhttp.newCall().await() 
    onFailure -> throw Exception
}

上述方法的挑战在于,它只启动函数pingServer()中存在的逻辑。
即函数makeNetworkCall()从未被触发。
如果我把第一个函数 Package 在一个新的协程作用域中,如下所示:
那么这两个作业都能正常工作,但是pingServer()抛出的异常永远不会被catch块捕获。

try {
   // With below logic, both functions are triggered 
   scope.launch { pingServer() } 

   makeNetworkCall()
} catch {
  // but pingServer() failure never reaches here
  // and makeNetworkCall() failure is properly captured here
}

如何并行运行上述两个长时间运行的任务,而不会相互阻塞?
我如何确保如果其中任何一个抛出异常,那么它会在上面所示的同一个catch块中被捕获?

bq3bfh9z

bq3bfh9z1#

基于this article,实现您所追求的目标的一种简单方法是不使用launch,而是使用async。然后,您可以运行makeNetworkCall(),然后在异步延迟对象上运行await,在那里捕获异常
就像这样:

suspend fun sendAllRequests() {
    try {
      val deferred = GlobalScope.async { pingServer() }
    
      makeNetworkCall()
      deferred.await()

    } catch(exception: CustomException) {
      // Do some other work and throw exception to UI
      throw RuntimeException("Custom Error")    
    }
}

还请注意:
1.你在另一个协程上启动或异步并不意味着代码在后台线程上运行,你需要将你的协程启动到另一个调度程序(例如:Dispatchers.IO)
1.避免使用GlobalScope,这样做很容易造成内存泄漏。创建您自己的作用域,或者如果您使用Android,请尝试使用AppComponents作用域,如viewModelScope或lifecycleScope

相关问题