【问题标题】:Will the coroutines bypass the flow?协程会绕过流程吗?
【发布时间】:2021-09-06 12:22:15
【问题描述】:

我正在使用协程进行网络调用,想知道是否有更好的方法来进行带有挂起的网络调用。

要求是:

  1. 调用网络 API
  2. 在收到响应之前暂停执行
  3. 根据 API 响应采取行动

活动:

private fun prepareUnlockUrl(url: String) {
        CoroutineScope(Dispatchers.IO).launch {
            val result = async { mViewModel.requestAccess(url) }

            when (result.await()) {
                Log.i(TAG, "do something with 'result'")
            }
        }
    }

ViewModel 包括:

suspend fun requestAccess(url: String): Response {
      return suspendCoroutine { continuation ->
            // Do network call and get response
            continuation.resume(response)
      }
}

【问题讨论】:

  • 取决于您使用的网络 API。它可能内置了对协程的支持。
  • 这是一个使用改造 + 存储库模式的内部 API

标签: kotlin kotlin-coroutines


【解决方案1】:

prepareUnlockUrl 有多个地方出错:

  1. 您不应该像这样动态创建协程范围。范围应该与某种生命周期相关联并适当地取消。

    • 如果您只需要在特定的协程上下文中运行某些东西,您可以改用withContext(Dispatchers.IO)。请注意,这是一个挂起函数,因此您只是在更改上下文,而不是在此处创建协程。
    • 如果你真的想创建一个协程(如果你不能让你的函数suspend),你应该已经有一个在活动中可用的即用型协程范围,例如lifecycleScope(查看this doc about coroutines in Android )
  2. 立即调用async 然后await() 是没有用的。

    • 如果您不通过async 调用更改协程上下文(这是您的情况),您可以直接调用挂起函数而不用包装在async 中,它会产生相同的效果
    • 如果您确实在 async 调用中使用了自定义协程上下文(您在这里没有使用),您可以简单地使用 withContext(someContext) { suspendCall } 而不是 async(someContext) { suspendCall() }.await()

所以最后我认为你应该写:

private suspend fun prepareUnlockUrl(url: String) = withContext(Dispatchers.IO) {
    mViewModel.requestAccess(url)
    Log.i(TAG, "do something with 'result'")
}

或者如果你因为某种原因不能让你的函数suspend,在现有的lifecycleScope中启动一个协程:

private fun prepareUnlockUrl(url: String) = lifecycleScope.launch(Dispatchers.IO) {
    mViewModel.requestAccess(url)
    Log.i(TAG, "do something with 'result'")
}

现在关于requestAccess,除非您应该如何准确地进行网络调用,否则我无法确定,但幸运的是,这与其他问题无关。

【讨论】:

  • 谢谢,选择协程的原因是等待requestAccess()的结果。如果可以达到目的,我更喜欢上面提到的第二种形式。
  • @Faisal 使用挂起函数已经在使用协程,区别在于我们在哪个级别启动协程来完成工作。通常的想法是尽可能保持挂起函数,然后在具有生命周期的顶级组件中启动协程,因此可以定义适当的协程范围(例如具有 lifecycleScopeviewModelScope 的组件)。
  • suspend 方法或suspendCoroutine 块访问共享首选项是否有任何限制?
  • @Faisal 对我的知识没有限制,但我有一段时间没有做过 Android 开发了 :D 应该没问题,因为你总是可以从挂起函数中调用任何函数(反之则不然)。也就是说,除非有特殊的线程限制,在这种情况下,您应该确保使用正确的调度程序使用withContext(例如,不要在主线程上使用commit(),而是使用apply())跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-30
  • 2013-06-02
  • 2022-09-22
  • 1970-01-01
  • 2021-01-04
  • 1970-01-01
相关资源
最近更新 更多