Fragment + viewModel 导致onChanged方法多次调用

问题描述:在使用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对象,该viewModel并非是创建,它获取的是该Fragment挂载的Activity那个ViewModel
        	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 + viewModel 导致onChanged方法多次调用_第1张图片
在整个过程中该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) {
            // ignore
            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对象?
如有问题欢迎斧正

你可能感兴趣的:(Android原生态开发,MVVC,java,android,开发语言)