Google maps 在viewmodelscope中创建ClickListener(或其他侦听器)可以吗?

Google maps 在viewmodelscope中创建ClickListener(或其他侦听器)可以吗?,google-maps,kotlin,android-room,kotlin-coroutines,android-viewmodel,Google Maps,Kotlin,Android Room,Kotlin Coroutines,Android Viewmodel,我有一个包含谷歌地图的片段,我在其中创建了一组标记(也可以单击)。它们添加了来自room livedata查询的不同信息(颜色、形状等)。此外,我还有一些MaterialButton按钮(样式为按钮),可以在其中打开标记可见状态。目前,SE标记的“设置”需要一些时间(200ms-2秒,取决于标记的数量)。为了摆脱这种等待,我计划使用viewmodelscope。由于其中定义了一些用于SE按钮的ClickListener(它们应该使用标记执行一些操作),因此当viewmodelscope协同程序部

我有一个包含谷歌地图的片段,我在其中创建了一组标记(也可以单击)。它们添加了来自room livedata查询的不同信息(颜色、形状等)。此外,我还有一些MaterialButton按钮(样式为按钮),可以在其中打开标记可见状态。目前,SE标记的“设置”需要一些时间(200ms-2秒,取决于标记的数量)。为了摆脱这种等待,我计划使用viewmodelscope。由于其中定义了一些用于SE按钮的ClickListener(它们应该使用标记执行一些操作),因此当viewmodelscope协同程序部分结束时,它们是否仍处于活动状态,如果它们处于活动状态,它们是否仍活在正确的协同程序上下文中,当片段和/或viewmodel结束时,我是否需要对侦听器进行一些整理

即:


我想你误解了什么是协同观测仪。它决定了它所运行的协同程序的生命周期,而不是在运行这些协同程序的过程中创建的对象的生命周期

viewModelScope
是一个协同作用域,当关联的ViewModel被拆除时,它会自动取消正在运行的任何协同作用域。协同程序不知道你在用它做什么。取消协同程序只会阻止它从运行到完成,就像提前从函数返回一样。在您的代码中,您设置了侦听器,并且没有将对它们的引用存储在它们设置为的视图中,因此它们的生命与各自视图的生命相关联

如果要在片段中使用协同程序为UI设置某些内容,则应使用片段的
lifecycleScope
,而不是ViewModel的
viewModelScope
。就像您正在获取要在UI中显示的内容一样,您希望在片段被销毁时取消协同路由,而不是在片段之外的ViewModel

在示例代码中使用协程看起来毫无意义,因为我没有看到调用任何阻塞或异步挂起函数。您提到设置站点标记需要大约200ms的时间。我不熟悉谷歌地图,因为我在过去几年中没有使用过它,所以我不确定哪一部分比较耗时。通常,UI元素不允许您在后台线程上与它们交互,因此您可能会运气不佳。但是,也许可以在后台线程上完成耗时的部分。你必须阅读文档。为此使用协同程序不会减少时间,但可以防止UI结巴/冻结

如果要使用协同程序进行一些长时间的计算,则需要切换dispatcher来执行阻塞工作,并与主dispatcher上的UI元素交互。简单地将某些内容放入协同程序不会减少所需的时间,但它提供了一种方便的方法,可以在另一个线程上执行某些操作,然后在结果就绪后在主线程上继续。例如:

lifecycleScope.launchWhenStarted { // lifecycle coroutines launch on main thread by default
    val result = withContext(Dispatchers.Default) { // switch to dispatcher for background work
        doTimeConsumingCalculation()
    }
    // back on main thread:
    applyResultsToMyViews(result) 
}

通过在启动时使用
launchWhenStarted
而不是
launch
,片段的
lifecycleScope
将在片段未连接时暂停协同程序,这将防止潜在的崩溃尝试使用
requireContext()
requireContext()更新UI
当没有活动时。

您所说的“viewmodelscope结束”是什么意思?ViewModel有一个名为
viewModelScope
的属性,它是一个用于运行协同程序的协同程序作用域,与您显示的代码无关。您已在活动的生命周期中检索到ViewModel,因此ViewModel的生命周期将与承载此片段的活动的生命周期相匹配。我实际上忘记了viewmodelscope行。。。很抱歉,我将很快重新引用我的Q。然后我添加了
viewModelScope.launch
。通过这种方式,我希望避免在标记设置和准备就绪时出现延迟。隐藏/取消隐藏可以很好地使用每个标记的可见参数(当观察按钮上选中的更改时,通过这些参数进行隐藏)。但是我担心,通过使用这个viewmodelscope,viewmodelscope结束时“基础”不在那里,所以当片段死亡时,片段不会杀死按钮上的onChangeListener…或者我需要一些“聪明的”
withContext
钳制,比如first
withContext(Dispatchers.Default){…}
viewModelScope内。启动
并在每次写入视图子体时使用
withContext(Dispatcher.Main){…}
。顺便说一句:你能用Dispatchers读取视图(或子视图)吗?主上下文(不影响(更改)UI。。。。我该如何更好地阻止这些合作。我已将viewmodel绑定到活动,而不是片段。因此,我可能会遇到viewmodel超出片段的问题,当片段消失时,对视图的引用将无效,因为viewmodel仍然活着(?)。Thanx回答得很好,这就清除了问题。我不知道
launchWhenStarted
,这可能有助于解决一些棘手的问题,但我想知道当使用launchWhenStarted时片段未附加时,您在片段中定义的侦听器是否也会“停止”。。。因此,如果您单击一个按钮(当然会与侦听器发生反应)并尝试在几乎相同的时间销毁该片段(即:将设备旋转180度),这是否也会停止侦听器?协同路由与由协同路由创建的对象的生命周期无关。无论它们是在标准函数中创建的,还是在任何类型的协同程序中创建的,都没有区别。当一个片段被销毁时,它的视图及其属性引用的所有内容都将被垃圾回收。这包括听众。该片段在主线程上被分解,您的视图侦听器也被称为o
lifecycleScope.launchWhenStarted { // lifecycle coroutines launch on main thread by default
    val result = withContext(Dispatchers.Default) { // switch to dispatcher for background work
        doTimeConsumingCalculation()
    }
    // back on main thread:
    applyResultsToMyViews(result) 
}