【问题标题】:Why functions that produce LiveData or Flow, don't have to be called from CoroutineScope?为什么生成 LiveData 或 Flow 的函数不必从 CoroutineScope 调用?
【发布时间】:2021-10-15 10:03:43
【问题描述】:

我们平时使用 Room 的时候,使用 Kotlin Coroutine 并做一个 DAO 来访问 Room 并得到结果。大多数函数通常在函数开头有suspend修饰符,但LiveDataFlow。 例如,让我们看一下下面的这两个代码。

@Query("SELECT * FROM MockTable")
suspend fun allMockDataWithSuspend(): List<MockData>

@Query("SELECT * FROM MockTable")
fun allMockData(): Flow<List<MockData>> // or LiveData<List<MockData>>

当我们使用suspend修饰符时,我们需要在协程范围内调用该函数,因为该函数具有suspend修饰符。但是当函数的结果是LiveDataFlow时,我们不需要在协程中调用函数,即使它是I/O访问。

这怎么可能?

【问题讨论】:

    标签: android android-room kotlin-coroutines android-livedata kotlin-flow


    【解决方案1】:

    挂起函数异步返回单个值,流返回多个异步计算值。

    流是冷的,没有活动的收集器就不会发出数据(流构建器中的代码在收集之前不会运行)。这是返回流的函数不是suspend 函数的一个关键原因,因为在调用任何终端运算符之前什么都不会发生。因此,在创建 Flow 时,没有人在收集它,因此没有任何工作正在完成。 尝试使用流或flowOf 函数创建flow,您会发现它们也不是suspend 函数。他们迅速返回,无需等待任何东西。中间操作员也不会触发流量收集,因此不会暂停功能,因为它们通常会将上游流转换为新流,它们与流本身一样冷。另一方面,终端操作符在启动流集合时是挂起函数。

    当涉及到LiveData 时,您有一个始终为空的初始值,Room 确保那些返回LiveData 的查询在后台线程上运行,而无需您手动执行。

    【讨论】:

      【解决方案2】:

      FlowLiveData 的 Dao 函数不会挂起,它们可以从任何地方调用。

      至于 即使它的 IO 访问,它是如何工作的?

      您需要了解async 编程的工作原理,async 编程背后的基本思想是callback。因此,您无需等待 IO 调用完成,而是注册一个回调,该回调将在响应准备好时调用。

      如果是 LiveDataFlow,您需要明确指定回调。函数调用不会阻塞(不需要线程卸载),它只是请求一些数据并注册一个回调,当数据准备好时应该调用它

      dao.allMockData().collect { data ->
           // This is the callback
      }
      
      dao.allMockData().observe(lifeCycleOwner, Observer {
          // This is the callback
      }
      

      如果是 suspend 函数,callback 是隐式的,kotlin 会负责它的创建和调用

      coroutineScope.launch{
           val data = allMockDataWithSuspend()
      }
      

      此函数调用将suspend 并且一旦从数据库中获得数据,将调用回调(隐式)并将响应存储在data 字段中。 kotlin 协程实现使它看起来像一个普通的旧顺序函数调用。

      【讨论】:

      • 你写的东西我看了好几遍了,首先我英文不太好,所以不知道怎么理解的好,问题是,Flow和LiveData被称为当数据准备好时,这就是为什么它们不需要暂停的原因,因为当数据准备好或发出时?当然是异步回调。
      【解决方案3】:

      你读过Flow documentation吗?它在那里进行了相当详细的解释。

      基本上(据我了解,我自己并没有长时间使用它们)您的挂起函数返回一个List,即一次性返回所有结果。如果生成该结果列表可能需要一些时间,您可以添加 suspend 关键字来表示这一点。然后通过调用该函数在协程中异步获取列表。

      Flows 是不同的 - 他们的重点是在任意时间提供结果,并且可能永远不会停止!数据项在它们发出时都会被传递,而不是在一个集合中一次全部传递。

      因此,当您创建Flow 时,您实际上还没有做任何工作。这就是为什么您的函数不是suspend 的原因,它只是在创建对象。要真正获取这些项目,您需要在其上调用collect,并且 需要在协程中发生,因为那是实际发生异步事情的地方。

      【讨论】:

        【解决方案4】:

        没有什么需要在协程上下文中创建 Flow 对象。只需创建最终异步生成数据的对象即可。

        Flow collection(异步获取实际的 Flow 结果)完全是另一回事。

        【讨论】:

        • 你的意思是当我使用Flow时,Flow会自动生成异步功能?所以这就是为什么我不需要使用挂起功能?
        • 从我链接到的 API 文档中可以看到,Flow 上的 collect 方法正在暂停。只有在实际需要数据时才需要暂停。不是在创建 Flow 对象时。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-11-10
        • 2020-10-27
        • 2014-02-13
        • 2019-01-18
        • 2013-04-09
        • 1970-01-01
        • 2018-11-08
        相关资源
        最近更新 更多