【问题标题】:How to replace LiveData with Flow如何用 Flow 替换 LiveData
【发布时间】:2021-04-15 00:33:39
【问题描述】:

我有一个名为sortOrderLiveData,然后我还有另一个名为myData 的变量,它观察sortOrder 的任何变化并相应地填充数据。

class TestViewModel @ViewModelInject constructor() : ViewModel() {

    private val sortOrder = MutableLiveData<String>()

    val myData = sortOrder.map {
        Timber.d("Sort order changed to $it")
        "Sort order is $it"
    }

    init {
        sortOrder.value = "year"
    }

}

在活动中观察

class TestActivity : AppCompatActivity() {

    private val viewModel: TestViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)
        
        // Observing data
        viewModel.myData.observe(this) {
            Timber.d("Data is : $it")
        }
    }
}

问题

  • 如何在不改变输出的情况下用Flow/StateFlow API 替换上述场景?

【问题讨论】:

  • 您是否可以选择使用 asLiveData 将 Flow 映射到 LiveData?
  • 是的,我可以在活动中做到这一点。问题是如果我将 StateFlow 用于 sortOrder,则每次重新创建活动时都会触发 map 主体。
  • @theapache64 为什么不在视图模型中使用 livedata 转换并将流作为 livedata 暴露给视图?
  • @Cyber​​Shark 重点是让虚拟机 liveData 免费。
  • 没有解决方案在行为上是正确的,除非它依赖于 SavedStateHandle,但获取 SavedStateHandle 更改通知的唯一安全方法是使用 SavedStateHandle.getLiveData。如果您想保持 LiveData-free,请不要使用 ViewModel。

标签: android kotlin kotlin-coroutines kotlinx.coroutines.flow


【解决方案1】:

从片段/活动的角度来看,您必须创建一个收集onStart() 中的流并在onStop() 中取消它的作业。使用lifecycleScope.launchWhenStarted 将保持流活动即使在后台。

使用bindin 库轻松迁移到Flow。我有偏见,尽管如此。

请参阅an article on medium 了解它。

【讨论】:

    【解决方案2】:

    如果您未能将映射的冷流转换为热流,它会在您每次收集它时重新启动流(例如重新创建您的 Activity 时)。这就是冷流的工作原理。

    我有一种感觉,他们会充实 StateFlow/SharedFlow 的转换函数,因为将它们映射到冷流并且必须将它们转换回热流感觉非常尴尬。

    如果您不想手动清晰地映射第一个元素,则公共流必须是 SharedFlow,因为stateIn 函数要求您直接提供初始状态。

        private val sortOrder = MutableStateFlow("year")
    
        val myData = sortOrder.map {
            Timber.d("Sort order changed to $it")
            "Sort order is $it"
        }.shareIn(viewModelScope, SharingStarted.Eagerly, 1)
    

    或者您可以创建一个单独的函数,在 mapstateIn 函数调用中调用。

        private val sortOrder = MutableSharedFlow<String>()
        
        private fun convertSortOrder(order: String): String {
            Log.d("ViewModel", "Sort order changed to $order")
            return "Sort order is $order"
        }
    
        val myData = sortOrder.map {
            convertSortOrder(it)
        }.stateIn(viewModelScope, SharingStarted.Eagerly, convertSortOrder("year"))
    

    【讨论】:

    • 哇。有用。非常感谢您的详细回答。我真的很感激。
    • 使用SharingStarted.Eagerly的任何具体原因?为什么不SharingStarted.Lazily
    • 我不认为在这种情况下会有所不同,因为无论如何您都会在 ViewModel 实例化之后立即收集它。如果它是您有时仅在用户执行某项操作时才收集的东西,那么您将决定是通过使其变得惰性来避免不必要的处理,还是通过使应用程序变得急切来使您的应用程序更快。
    猜你喜欢
    • 2021-06-29
    • 1970-01-01
    • 2020-03-12
    • 1970-01-01
    • 2020-03-05
    • 1970-01-01
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    相关资源
    最近更新 更多