ViewModel onchange gets called multiple times when back from Fragment
here is what you are doing wrong...
viewmModel.observeData().observe(getActivity(), new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
if(s.equals("0")) {
BlankFragment fragment = (BlankFragment) manager.findFragmentByTag(DETAIL_FRAG);
if (fragment == null) {
fragment = BlankFragment.newInstance();
}
addFragmentToActivity(manager,
fragment,
R.id.root_activity_detail,
DETAIL_FRAG
);
} else {
Toast.makeText(getContext(), "Wrong text", Toast.LENGTH_SHORT).show();
}
}
});
in above code instead of "getActivity()" either you can use "this" or "viewLifecycleOwner".
Because as you are passing the getActivity() in observe method, whenever you open your fragment you are attaching the new instance of the observer with the Activity not with the fragment. So observer will keep alive even if you kill your fragment. So when livedata postvalue, it will send data to all the observers, as there are too many observers observing livedata, then all will get notified. Because of this, your observer gets called too many times. so you have to observe live data in fragment something like this.
viewmModel.observeData().observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
if(s.equals("0")) {
BlankFragment fragment = (BlankFragment) manager.findFragmentByTag(DETAIL_FRAG);
if (fragment == null) {
fragment = BlankFragment.newInstance();
}
addFragmentToActivity(manager,
fragment,
R.id.root_activity_detail,
DETAIL_FRAG
);
} else {
Toast.makeText(getContext(), "Wrong text", Toast.LENGTH_SHORT).show();
}
}
});
But still your onchanged method will get called two times.
You can stop this by checking one condition inside your onchanged method..
dash_viewModel.getDashLiveData().observe(viewLifecycleOwner, object : Observer<AsyncResponse> {
override fun onChanged(t: AsyncResponse?) {
if(viewLifecycleOwner.lifecycle.currentState==Lifecycle.State.RESUMED){
setData(t)
}
}
})
from my research, I have found out that, if fragment using the ViewModel of its corresponding activity, So when even you start observing the livedata, it will first send you the most recently emitted item. even if you didn't call it from your fragment.
so onChange method got called two times
When the fragment is on start state - to receive the most recently emitted item
When the fragment is on Resumed state - to receive the call made by fragment either for api.
so on changed I always check the state of the fragment with the help of viewLifecycleOwner like this
if(viewLifecycleOwner.lifecycle.currentState==Lifecycle.State.RESUMED){
// if the fragment in resumed state then only start observing data
}
viewlifecycleowner is provided by both Fragments and Activity as Google implemented this solution directly in support library 28.0.0 and androidx with getViewLifecycleOwner() method. viewlifecycleowner contains info about the lifecycle of the component.
in java you can use getViewLifecycleOwner() intead of viewlifecycleowner .
The problem here is that when you dettach the fragment from the acitivity, both fragment and its viewmodel are not destroyed. When you come back, you add a new observer to the livedata
when the old observer is still there in the same fragment (If you add the observer in onCreateView()
).
There is an article (Even a SO thread in fact) talking about it (with solution).
The easy way to fix it (also in the article) is that remove any observer from the livedata before you add observer to it.
Update: In the support lib v28, a new LifeCycleOwner called ViewLifeCycleOwner should fix that more info in here
Here is an example how i solve this problem .[TESTED AND WORKING]
viewModel.getLoginResponse().observe(getViewLifecycleOwner(), new Observer<String>() {
@Override
public void onChanged(String response) {
if(getViewLifecycleOwner().getLifecycle().getCurrentState()== Lifecycle.State.RESUMED){
// your code here ...
}
}
});
Instead of using getActivity
as LifecycleOwner, you should use fragment.
Change
viewModel.observeData().observe(getActivity(), new Observer<String>() {
to
viewModel.observeData().removeObservers(this);
viewModel.observeData().observe(this, new Observer<String>() {