未调用 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 实施)观察者
在 xml 中设置/观察 livedata 的正确方法是啥?调用观察者方法失败