【问题标题】:Android - Communication between two Fragments via a shared ViewModelAndroid - 通过共享 ViewModel 在两个 Fragment 之间进行通信
【发布时间】:2020-09-20 23:59:22
【问题描述】:

我有一个Fragment A,它显示了一个项目列表。单击一个项目时,我们会导航到Fragment B,它会显示有关所选项目的更详细视图。所以,我需要在Fragment AB 之间进行某种交流。

在文档中(请参阅 here ),他们声明共享的 ViewModel 用于片段之间的通信是推荐的方式。所以,像这样实现它:

class SharedViewModel : ViewModel() {

    // used to share Item between list and detail view
    private val _selectedItem = MutableLiveData<Item>()
    
    // Fragment B listens for this LiveData variable
    val selectedItem : LiveData<Item> = _selectedItem
    
    // called by the Fragment A which passes the selected item as argument
    fun selectItem(item: Item) {
        _selectedItem.value = item
    }
}

Fragment B 中,我们像这样监听selectedItem LiveData 变量:

....
// I use a global variable here cuz I want to use it in several different methods
private lateinit var selectedItem : Item
....
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    // listen for the selected item
    sharedViewModel.selectedItem.observe(viewLifecycleOwner, Observer { item ->
        selectedItem = item
    })
}

现在,在onStart()我有一些操作只有在selectedItem被初始化时才能操作:

override fun onStart() {
    super.onStart()
    
    doSomethingWithSelectedItem(selectedItem) //<--- CRASH
    doAnotherThingWithSelectedItem(selectedItem)  // probably it would crash here, too 
}

此时,我得到selectedItem lateinit var 变量未初始化的异常。所以,我假设此时onViewCreated()(即selectedItem = item)中的设置尚未生效。

所以,我的问题是:当您想要初始化一个 lateinit 变量时,哪个生命周期方法是最好的,该变量的值通过两个 Fragment 之间的 ViewModel 共享?

【问题讨论】:

  • 为什么你首先要有一个 lateinit 引用?我的建议是将doAnotherThingWithSelectedItem 移动到观察者并删除变量item
  • 你在observe上做你想做的操作,而不是onStart。你甚至不需要一个全局变量,更不用说lateinit,如果你只是在观察时将数据传递给那些方法

标签: android android-fragments android-viewmodel


【解决方案1】:

我认为问题在于 LiveData 的 observe() 方法中的函数没有立即执行。 OnViewCreated() 是在 onStart() 之前执行的方法。如果 LiveData observe() 方法立即执行该函数,您不会有任何问题。 observe() 方法的作用是将 LiveData 对象设置为观察其值的变化,而无需立即执行定义的函数。

我要做的是在Fragment B 中编写这段代码:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    selectedItem = sharedViewModel.selectedItem.value
}

在你的情况下,我认为你甚至不需要使用 LiveData 对象,因为你只想获取一个值,只要你在同一个片段中,这个值就不会改变。

【讨论】:

    【解决方案2】:

    片段的 onStart() 生命周期在 onViewCreated() 之前调用 片段的生命周期。

    观察 onstart 中的 livedata 并设置 lateinit 变量。取决于你的函数做什么,你可以在设置 lateinit 变量后在 onStart() 中调用它们,或者在 onViewCreated() 中调用它们

    【讨论】: