未调用 LiveData 观察者

Posted

技术标签:

【中文标题】未调用 LiveData 观察者【英文标题】:LiveData Observer not Called 【发布时间】:2018-07-22 17:32:23 【问题描述】:

我有一个活动,TabBarActivity,它承载了一个片段,EquipmentRecyclerViewFragment。片段接收 LiveData 回调,但 Activity 没有(如调试模式下的断点所证明的那样)。奇怪的是,如果我调用 ViewModel 的 initData 方法,Activity 回调确实会触发。以下是上述组件的相关部分:

TabBarActivity

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    initVM()
    setContentView(R.layout.activity_nav)
    val equipmentRecyclerViewFragment = EquipmentRecyclerViewFragment()
    supportFragmentManager
            .beginTransaction()
            .replace(R.id.frameLayout, equipmentRecyclerViewFragment, equipmentRecyclerViewFragment.TAG)
            .commit()
    navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)



var eVM : EquipmentViewModel? = null
private fun initVM() 
    eVM = ViewModelProviders.of(this).get(EquipmentViewModel::class.java)
    eVM?.let  lifecycle.addObserver(it)  //Add ViewModel as an observer of this fragment's lifecycle
    eVM?.equipment?.observe(this, loadingObserver)//        eVM?.initData() //TODO: Not calling this causes Activity to never receive the observed ∆

val loadingObserver = Observer<List<Gun>>  equipment ->
    ...

EquipmentRecyclerViewFragment

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    columnCount = 2
    initVM()


//MARK: ViewModel Methods
var eVM : EquipmentViewModel? = null
private fun initVM() 
    eVM = ViewModelProviders.of(this).get(EquipmentViewModel::class.java)
    eVM?.let  lifecycle.addObserver(it)  //Add ViewModel as an observer of this fragment's lifecycle
    eVM?.equipment?.observe(this, equipmentObserver)
    eVM?.initData()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? 
    val view = inflater.inflate(R.layout.fragment_equipment_list, container, false)
    if (view is RecyclerView)  // Set the adapter
        val context = view.getContext()
        view.layoutManager = GridLayoutManager(context, columnCount)
        view.adapter = adapter
    
    return view

设备视图模型

class EquipmentViewModel(application: Application) : androidViewModel(application), LifecycleObserver 
var equipment = MutableLiveData<List<Gun>>()
var isLoading = MutableLiveData<Boolean>()

fun initData() 
    isLoading.setValue(true)
    thread  Thread.sleep(5000) //Simulates async network call
        var gunList = ArrayList<Gun>()
        for (i in 0..100)
            gunList.add(Gun("Gun "+i.toString()))
        
        equipment.postValue(gunList)
        isLoading.postValue(false)
    

最终目标是让活动只观察 isLoading MutableLiveData 布尔值,但由于这不起作用,我将活动更改为只观察设备 LiveData 以最大限度地减少变量的数量。

【问题讨论】:

尝试在片段中传递 ViewModelProviders.of(getActivity) 而不是“this” 做到了!我将我的片段initVM() 代码更改为val myActivity = activity ?: return eVM = ViewModelProviders.of(myActivity).get(EquipmentViewModel::class.java),它触发了两者。如果您能解释为什么它在答案中起作用,我会将其标记为已接受并支持。 使用viewLifecycleOwner怎么样? 【参考方案1】:

要获得与Activity 相同的ViewModel 引用,您需要传递相同的Activity 实例,您应该使用ViewModelProviders.of(getActivity)。当您将this 作为参数传递时,您会收到与您的Fragment 关联的ViewModel 实例。

有两种重载方法:

ViewModelProvider.of(Fragment fragment)

ViewModelProvider.of(FragmentActivity activity)

欲了解更多信息Share data between fragments

【讨论】:

我认为关键是只有一个 ViewModel?为什么是新实例? 重点是每个组件只有一个ViewModel,每个Fragment 或宠物Activity 谢谢!我快疯了。在我的例子中,我在片段中使用了viewModelStore,所以我需要做的就是使用requireActivity().viewModelStore从活动中获取它。【参考方案2】:

我把这段代码放在onActivityCreated 片段中,不要小看getActivity ;)

if (activity != null)             
     globalViewModel = ViewModelProvider(activity!!).get(GlobalViewModel::class.java)
    


globalViewModel.onStop.observe(viewLifecycleOwner, Observer  status ->
            Log.d("Parent Viewmodel", status.toString())
        )

这段代码帮助我监听片段中父视图模型的变化。

【讨论】:

【参考方案3】:

仅适用于对 SharedViewModel 的定义与让两个片段使用一个视图模型的定义感到困惑的人:

SharedViewModel 用于共享“数据”(想象两个新实例被创建,视图模型中的数据被发送到两个片段),它不用于可观察对象,因为可观察对象寻找“相同”实例来采取行动。这意味着您需要为两个片段创建一个视图模型实例。

IMO:Google 应该在他们的文档中以某种方式提及这一点,因为我自己认为在引擎盖下它们是相同的实例,但它基本上不是,现在它实际上是有意义的。

编辑: Kotlin 中的解决方案: 2021 年 11 月 25 日

在您的活动中 -> val viewModel : YourViewModel by viewModels()

在片段 1 - >

val fragmentViewModel =
                ViewModelProvider(requireActivity() as YourActivity)[YourViewModel::class.java]

在片段 2 中 - >

val fragmentViewModel =
                ViewModelProvider(requireActivity() as YourActivity)[YourViewModel::class.java]

这种方式 2 片段共享一个 Activity 视图模型实例,并且两个片段都可以使用侦听器来观察它们之间的变化。

【讨论】:

很好的解释! 最佳答案【参考方案4】:

当您创建片段而不是通过 viewModels() 获取 viewModel 对象时,从 activityViewModels()

获取它
import androidx.fragment.app.activityViewModels

class WeatherFragment : Fragment(R.layout.fragment_weather) 

    private lateinit var binding: FragmentWeatherBinding
    private val viewModel: WeatherViewModel by activityViewModels() // Do not use viewModels()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View 
        binding = FragmentWeatherBinding.inflate(inflater, container, false)

        binding.viewModel = viewModel

        // Observing for testing & Logging
        viewModel.cityName.observe(viewLifecycleOwner, Observer 
            Log.d(TAG, "onCreateView() | City name changed $it")
        )
        return binding.root
    

【讨论】:

【参考方案5】:

Kotlin 答案

如果您正在使用,请删除函数中的这两点:

    = viewModelScope.launch 暂停

【讨论】:

以上是关于未调用 LiveData 观察者的主要内容,如果未能解决你的问题,请参考以下文章

LiveData 未通知我的服务(LifecyclerOwner 实施)观察者

Android Room LiveData观察器未更新

在 xml 中设置/观察 livedata 的正确方法是啥?调用观察者方法失败

为啥观察 LiveData 时不调用 onChanged()

LiveData详解

Android LiveData粘性,粘连,倒灌