【问题标题】:LiveData Observer not Called未调用 LiveData 观察者
【发布时间】: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怎么样?

标签: android android-fragments mvvm kotlin android-livedata


【解决方案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 答案

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

      1. = viewModelScope.launch { }
      2. 暂停

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-02-04
        • 2019-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多