问题描述:在使用Fragment + ViewModel时如果进行Fragment切换时,即Fragment的生命周期由onDestroyView再到onCreateView时。如果ViewModel数据发生改变,则会导致onChanged方法多次执行。
伪码如下:
public class MyFragment extends Fragment {
private MyViewModel viewModel;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
···
viewModel=
new ViewModelProvider(getActivity()).get(MyViewModel .class);
···
init();
}
init(){
viewModel.getData().observe(getViewLifecycleOwner(), new Observer<String>() {
@Override
public void onChanged(String s) {
···
}
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
上述伪码会导致onChange方法多次执行。通过debugger不难发现,在进行页面切换并回切的过程中,首先执行onDestroyView方法然后再次执行onCreateView方法。根绝Fragment的生命周期: 在整个过程中该Fragment并为被销毁,通俗来说,onCreateView方法一直是就一个对象再执行,那么observe方法也在切换的过程中重复执行,该方法的注解如下:
* Adds the given observer to the observers list within the lifespan of the given
* owner. The events are dispatched on the main thread. If LiveData already has data
* set, it will be delivered to the observer.
*
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
}
也就是说将一个观察者添加到观察者列表中,再这个过程中owner进过了进一步处理,观察者列表为Map形式,由于 owner.getLifecycle().addObserver(wrapper);方法添加的是被封装的LifecycleBoundObserver 对象,而且该类并为重写equip方法,因此每次添加的观察者的hash值都不相同,也就是说观察者列表允许添加重复的观察者对象。 回到刚才的问题。重复执行observe方法,就会重复将该观察者添加到观察者列表中,因此onchange方法也会重复执行。
解决方法
其实解决该问题只需要在调用onDestroyView方法时将已经注册的观察者从观察者列表中移出便可。具体方法如下:
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
viewModel.getData().removeObservers(getActivity());
}
思考:viewModel的观察者列表是否会导致内存泄漏?移出观察者是不是就可以避免ViewModel导致的内存泄漏问题。
解决方法2
在fragment中注册贯彻者时不要使用getActivity()作为观察者对象(本身也不建议这么使用,如果注册activity为观察者对象,Frament移除时观察者依旧能观察)使用以下代码:
.observe(getViewLifecycleOwner(),e->{});
方法2思考:使用getActivity()作为观察者,页面切换回来后,如果onChanged方法中调用了databing,那么再次调用onChanged方法时会导致databing为null异常。该问题可能是由于观察者内部不存在databing对象? 如有问题欢迎斧正
|