未调用 LiveData 观察器
LiveData Observer not Called
我有一个 activity,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
}
EquipmentViewModel
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)
}
}
最终目标是让 activity 只观察 isLoading
MutableLiveData 布尔值,但由于那不起作用,我将 activity 更改为只观察设备 LiveData 到尽量减少变量的数量。
要获得对 Activity
的 ViewModel
的相同引用,您需要传递相同的 Activity 实例,您应该使用 ViewModelProviders.of(getActivity)
。当您将 this
作为参数传递时,您会收到与您的 Fragment
.
关联的 ViewModel
实例
有两个重载方法:
ViewModelProvider.of(Fragment fragment)
ViewModelProvider.of(FragmentActivity activity)
我把这段代码放在了onActivityCreated
片段里面,别小看getActivity ;)
if (activity != null) {
globalViewModel = ViewModelProvider(activity!!).get(GlobalViewModel::class.java)
}
globalViewModel.onStop.observe(viewLifecycleOwner, Observer { status ->
Log.d("Parent Viewmodel", status.toString())
})
此代码帮助我监听片段中父 ViewModel 的变化。
Kotlin 答案
Remove these two points in your function if you are using:
- = viewModelScope.launch { }
- 暂停
当您创建片段而不是通过 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
}
}
仅供那些在 SharedViewModel 定义与使两个片段使用一个视图模型之间感到困惑的人:
SharedViewModel 用于共享 'DATA'(假设正在创建两个新实例并将视图模型中的数据发送到两个片段)它不用于可观察对象,因为可观察对象寻找 'SAME'采取行动的实例。这意味着您需要为两个片段创建一个视图模型实例。
IMO:Google 应该在他们的文档中以某种方式提及这一点,因为我自己认为在引擎盖下它们是相同的实例,但它基本上不是,现在实际上是有道理的。
编辑: Kotlin 中的解决方案: 11/25/2021
在你的 activity -> 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]
This Way 2 个片段共享一个 Activity 视图模型实例,两个片段都可以使用侦听器来观察它们之间的变化。
我有一个 activity,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
}
EquipmentViewModel
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)
}
}
最终目标是让 activity 只观察 isLoading
MutableLiveData 布尔值,但由于那不起作用,我将 activity 更改为只观察设备 LiveData 到尽量减少变量的数量。
要获得对 Activity
的 ViewModel
的相同引用,您需要传递相同的 Activity 实例,您应该使用 ViewModelProviders.of(getActivity)
。当您将 this
作为参数传递时,您会收到与您的 Fragment
.
ViewModel
实例
有两个重载方法:
ViewModelProvider.of(Fragment fragment)
ViewModelProvider.of(FragmentActivity activity)
我把这段代码放在了onActivityCreated
片段里面,别小看getActivity ;)
if (activity != null) {
globalViewModel = ViewModelProvider(activity!!).get(GlobalViewModel::class.java)
}
globalViewModel.onStop.observe(viewLifecycleOwner, Observer { status ->
Log.d("Parent Viewmodel", status.toString())
})
此代码帮助我监听片段中父 ViewModel 的变化。
Kotlin 答案
Remove these two points in your function if you are using:
- = viewModelScope.launch { }
- 暂停
当您创建片段而不是通过 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
}
}
仅供那些在 SharedViewModel 定义与使两个片段使用一个视图模型之间感到困惑的人:
SharedViewModel 用于共享 'DATA'(假设正在创建两个新实例并将视图模型中的数据发送到两个片段)它不用于可观察对象,因为可观察对象寻找 'SAME'采取行动的实例。这意味着您需要为两个片段创建一个视图模型实例。
IMO:Google 应该在他们的文档中以某种方式提及这一点,因为我自己认为在引擎盖下它们是相同的实例,但它基本上不是,现在实际上是有道理的。
编辑: Kotlin 中的解决方案: 11/25/2021
在你的 activity -> 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]
This Way 2 个片段共享一个 Activity 视图模型实例,两个片段都可以使用侦听器来观察它们之间的变化。