Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LiveData Observer not Called

Tags:

I have an activity, TabBarActivity that hosts a fragment, EquipmentRecyclerViewFragment. The fragment receives the LiveData callback but the Activity does not (as proofed with breakpoints in debugging mode). What's weird is the Activity callback does trigger if I call the ViewModel's initData method. Below are the pertinent sections of the mentioned components:

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)     } } 

The ultimate aim is to have the activity just observe the isLoading MutableLiveData boolean, but since that wasn't working I changed the activity to observe just the equipment LiveData to minimize the number of variables at play.

like image 795
James Jordan Taylor Avatar asked Feb 12 '18 14:02

James Jordan Taylor


People also ask

What is LiveData Observer?

LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.

Can ViewModel observe LiveData?

[...] However ViewModel objects must never observe changes to lifecycle-aware observables, such as LiveData objects.

Where do you observe LiveData in activity?

liveData declared as val can't change, but you can always update the value of LiveData because it is MutableLiveData . Any change in value of liveData notifies all of its active observers. LiveData is always observed inside a UI Lifecycle owner, which can be an Activity or a Fragment.


2 Answers

To get same reference of ViewModel of your Activity you need to pass the same Activity instance, you should use ViewModelProviders.of(getActivity). When you pass this as argument, you receive instance of ViewModel that associates with your Fragment.

There are two overloaded methods:

ViewModelProvider.of(Fragment fragment)  ViewModelProvider.of(FragmentActivity activity) 

For more info Share data between fragments

like image 119
Pavel Poley Avatar answered Oct 24 '22 04:10

Pavel Poley


I put this code inside the onActivityCreated fragment, don't underestimate getActivity ;)

if (activity != null) {                  globalViewModel = ViewModelProvider(activity!!).get(GlobalViewModel::class.java)     }   globalViewModel.onStop.observe(viewLifecycleOwner, Observer { status ->             Log.d("Parent Viewmodel", status.toString())         }) 

This code helps me to listening Parent ViewModel changes in fragment.

like image 35
iamkdblue Avatar answered Oct 24 '22 03:10

iamkdblue