Ben*_*n K 1 data-binding kotlin android-livedata kotlin-flow kotlin-stateflow
我正在尝试弄清楚流/状态流和数据绑定。
\n在我的项目中,我有 Room 中的“打印机”列表,以及 SharedPreferences 中的“默认打印机”的 ID。
\n\n我是否使用了良好的流程并做得很好,还是出了什么问题?
\n房间给我一个Flow<List<Printer>>。借助我的适配器,我将其显示在回收器视图中viewModel.allPrinters.collect。
但是,当我想从房间绑定资源时,绑定“需要”LiveData 或 StateFlow,这是我的困难,因为 stateIn 需要协程。
\n我怎样才能从我的默认打印机Flow<List<Printer>>(通过他的属性“id”在列表中找到它)并确保列表/项目是否更新,我的参考也将获得更新?\n如果我更改打印机的名称defaultPrinter并将其更新到 room(感谢我的存储库/room DAO),我希望在我的对象defaultPrinter和列表中更改名称。
您还可以确认我的变量类型defaultPrinter应该是StateFlow<Printer?>
注意:当我更新项目时,printerRoomDAO.update(item:Printer)我必须传递数据对象的副本Printer(新引用),否则我的 UI 将不会更新。
isPrinterListEmpty这是我的 ViewModel 中的示例
val hasNoPrinter = allPrinters.mapLatest {\n it.isEmpty()\n}.asLiveData()\nRun Code Online (Sandbox Code Playgroud)\n它运行良好,但我不应该使用 StateFlow 吗?怎么做 ?因为stateIn()需要一个协程,而我下面的尝试不起作用。
val hasNoPrinter = runBlocking {\n allPrinters.mapLatest {\n it.isEmpty()\n }.stateIn(viewModelScope)\n}\nRun Code Online (Sandbox Code Playgroud)\n我尽量不让您感到困惑,感谢您的帮助。\n最诚挚的问候。
\ncac*_*acs 11
首先了解一些术语可能会有所帮助
AFlow是一个产生值流的东西 - 有点像 a Sequence,但是异步的。随着时间的推移,您会得到这些值。每次您致电collect()(或其他终端操作员)时Flow,都会重新开始。这些消费者之间没有共享状态,流程独立运行。
那么如果您确实想要一些共享状态怎么办?Flow如果您想要多个消费者可以观察到的单个流,而不是拥有单独的 a 实例,该怎么办?您有两个主要选项 - aSharedFlow和 a StateFlow。
ASharedFlow顾名思义就是多个消费者(订阅者)可以观察到的单个数据流。当更新发生时,每个订阅者都会看到新值。
当您配置一个(例如使用shareIn)时,您可以设置一个replay值 - 这基本上是存储的旧值的数量,新订阅者在开始从 收集时将收到这些旧值SharedFlow。如果将其设置为0,则在下一个新值到达之前他们不会看到任何值。如果设置为1,他们将看到最新的项目(这与 a 的行为类似LiveData)。如果将其设置为更高的数字,订阅者将获得该数量的过去事件(如果有)。
AStateFlow类似 - 它基本上是值为 1 的 a SharedFlow,因此replay它始终向订阅者提供最新的值。它还需要一个初始值,因为它意味着一种状态- 许多可能值之一,而不是记住上游生成的最后一项(如果有)。(所以这就像设置了默认值一样。)LiveData
你使用哪一个取决于你想要什么——你是否公开了一个状态,其中必须有一些值来代表它?或者只是一个价值流,它可能已经开始,也可能尚未开始?你有默认值吗?如果您正在考虑null,也许您不需要!
NormalFlow是冷的- 它们不会做任何事情,直到消费者调用collect它们上的终端操作符(例如 ),或者Flow它们的下游(因此“开始生产”调用向上传播)。因此,它们只有在有东西观察它们时才会运行,一旦观察被取消,它们就会Flow停止产生。
SharedFlow不过,它们StateFlow都很热门——无论订阅者是否观察它们,它们都会存在,并且它们总是观察自己的上游流量,因此它们可以收集这些最近的值,以便为任何需要它们的订阅者存储。这意味着,如果您创建一个SharedFlow或,只要生产者存在,StateFlow它们就会使生产者保持活力。
所以你需要一种方法来控制它们运行的时间!这就是/构建器CoroutineScope中的用途。它不是一个协程,它是一个组织性的东西,你可以在内部启动协程,它允许你控制诸如它们的生命周期之类的东西。当您完成范围后,您可以取消它,这就像在说“好吧,这里正在工作的所有东西 - 打包,我们完成了”。shareInstateInCoroutineScope
您可能已经使用过其中的一些 - viewLifecycleOwner.lifecycleScopein aFragment是一个只要当前视图层次结构存在就存在的范围。一旦拆除,系统就会自动为你取消Lifecycle范围。这使您可以运行专门处理这些视图的协程,而不会超过它们的寿命 - 创建新的视图层次结构时,您将启动一组新的协程来处理这些视图。
viewModelScope只要ViewModel存在就持续存在,这比 a 中的视图更长Fragment。这允许您在虚拟机中创建运行时间更长的协程,Fragment当您旋转设备或在应用程序中导航时,这些协程可以在娱乐等活动中持续存在。因此,这对于长时间运行的工作以及维护状态(例如在 a 中StateFlow)之类的事情很有好处,这些状态的生成成本很高,例如通过网络调用。
因此,您需要向这些构建者提供其中一个,以便这些热流CoroutineScope具有某种终身感。这里有一篇来自一位 Android 开发人员的文章,介绍了如何创建自己的作用域 - 实际上只是创建一个作用域,并保留对其的引用,以便在需要停止其中的所有工作时可以取消它。
推荐的方法是创建一个应用程序范围的作用域,当应用程序的进程被销毁时,该作用域将被拆除 - 这样,您在该作用域中运行的任何内容都将在应用程序的生命周期内保留(除非其工作更快结束)。因此,如果您有想要维护的东西,并且可能需要不断运行,那么您就可以做到这一点!如果它的作用域应该限定为 a 的生命周期ViewModel(即仅在创建使用时才运行),那么您应该使用该作用域。
因此,就您的打印机而言,如果您有打印机,那么这是RoomallPrinters Flow查询的结果:
// cold Flow
val defaultPrinter = allPrinters.map { printers ->
printers.firstOrNull { it.id == user.id }
}
// hot flow
val defaultPrinter = allPrinters.map { printers ->
printers.firstOrNull { it.id == user.id }
}.stateIn(someScope, SharingStarted.Lazily, initialValue = null)
Run Code Online (Sandbox Code Playgroud)
因此,它的作用是将所有List<Printer>传入的值映射到单个值Printer?,并将最新值存储在 a 中StateFlow。我使用状态是因为这是一个要么存在要么不存在的东西,所以它要么是“此打印机”状态,要么是“无打印机”状态,并且我使用null作为默认值 - “无打印机”。
查看这些内容的文档SharingStarted,但Lazily基本上意味着在第一个订阅者出现之前它不会执行任何打印机检查。如果您希望它立即开始获取值,则可以使用Eagerly它,这样当第一个订阅者出现时它就会“预热”。这scope就是我们之前讨论过的 - 您可以将一个注入到存储库中,或者如果它defaultPrinter StateFlow存在于 a 中ViewModel,您可以直接使用,viewModelScope因为它的存在(和生命周期)与虚拟机相关。
说起来很长,希望对你有帮助。如果有人对这里的任何内容有任何指示或更正,请告诉我 - 我自己仍在争论其中的一些问题(解释它也有助于我更好地理解事情)
| 归档时间: |
|
| 查看次数: |
2643 次 |
| 最近记录: |