在刚接触LifeCycle组件中的ViewModel的时候就有一个疑问,这个ViewModel是不是MVVM中的ViewModel。解决这个疑问首先要了解MVVM是什么。
1. MVVM
MVVM即 Model-View-ViewModel,和MVP一样是一种开发模式,唯一的不同就是MVVM可以实现 View和ViewModel的双向绑定。其实我不太懂这个“双向绑定”,很多人说双向绑定就是view变化可以直接通知model,model变化可以直接通知到view,在MVP模式中也是啊,view变化调用presenter方法改变model,model变化调用presenter的方法改变view。我认为真正的双向绑定是不用写代码去监听就实现的绑定,在Android中这个工具就是dataBinding,这篇我们不讲databinding。
2. ViewModel
所以使用ViewModel并不是单纯的为了MVVM模式,另外一个重要的功能是保持数据。当Activity重建的时候我们通常使用onSaveInstance方法保存数据,但是这个会增加额外的开发工作量,ViewModel自动就有这个功能,当Activity意外被销毁重建的时候,ViewModel中的数据不会丢失。这张是官方给的ViewModel生命周期:
当Activity旋转的时候,activity会被销毁重建,如果我们不做保存数据的处理的话,数据肯定都会被重置,如果存在ViewModel中的话这个出具是可以被保存下来的,这就是ViewModel的主要优势。
3. LiveData
官方说明 LiveData是一个可观察的数据持有类,也就是说是一个被观察者,他有什么好处呢,这是官方列出的几大好处
1. Ensures your UI matches your data state
确保你的UI和你的数据状态匹配,就是说只要监听到数据的变化就可以去更新UI,因为内部处理了生命周期的问题,只要有通知更新UI就一定没有问题。
2. No crashes due to stopped activities
当Activity销毁的时候不会产生崩溃,当我们用普通的MVP模式进行开发的时候,在Activity销毁是通常会手动取消掉网络请求,稍有不慎就可能出现崩溃,但是使用LiveData基本是绝对安全的,只有在LifeCyclerOwner为active(onStart, onResume)的时候才会回调,不用担心网络求情回来的时候界面被销毁导致空指针的问题。
3. Always up to date data
数据会自动更新,上面说只有在active的时候才会回调,但是如果在inactive的时候来了数据怎么办,LiveData可以在LifeCyclerOwner的状态变为active的时候自动将自己持有的data通知给LifeCyclerOwner。
4. Proper configuration changes
当屏幕旋转导致Activity或者Fragment重建时,LivaData可以自动将自己持有的数据更新给UI组件。
4. ViewModel + LiveData会碰撞出怎样的火花
比如这个例子,第一个TextView使用ViewModel+LiveData,通常两个东西一块使用,第二个是普通数据,就是在Activity里保存数据,第三个是只用也是ViewModel + LiveData,只不过数据源保存到了Activity中,第四个是在Activity中实例化 LivaData
点击右下角的“+”,四个数据一块变化
当屏幕旋转时:
可以看出当屏幕旋转时如果不做数据保存的工作普通数据就会丢失,用ViewModel+LiveData (ViewModel保存数据)和 ViewModel+LiveData (Activity保存数据)的数据都又显示了出来,这得益于上面提到的LiveData的第四个优点:当屏幕旋转导致Activity或者Fragment重建时,LivaData可以自动将自己持有的数据更新给UI组件,但是当LiveData在Activity中实例化的时候,这种情况下LiveData本身就被销毁了,所以就不会自动更新。
当再次点击加号的时候:
只有ViewModel持有的数据继续增加,其他都从1开始了,说明ViewModel确实在屏幕旋转的时候能保存数据。
5. 疑问
5.1 ViewModel是怎么做到保存数据的
解决这个疑问首先从ViewModel的实例化说起
final ViewModelImp viewModel = ViewModelProviders.of(this).get(ViewModelImp.class);
首先看 of这个方法:
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
最终生成了一个ViewModelProvider实例,然后调用get方法
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
这段代码就是首先从mViewModelStore里面拿,如果存在了就直接取出来,否则就生成一个,所以说Activity销毁重建而ViewModel没有销毁的核心就在于这个mViewModelStore没有销毁,而这个mViewModelStore是通过activity.getViewModelStore()拿到的
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
这段代码似乎能看出玄机,首先如果Activity的mViewModelStore为null就去NonConfigurationInstances里面拿
Activity的这个参数是在attache的时候传过来的,我们都知道这个方法是在ActivityThread里的performLaunchActivity的时候调用的
最终发现这个东西是在startActivityNow的时候调用的。这表明mViewModelStore这个实例的生命周期是由ActivityThread及其上层控制的,所以ViewModel的生命周期比Activity的要长就不难理解了,Activity重建之后AvtivityThread传过来之前保存的ViewModelStore,在配置改变时会调用这个方法保存ViewModelStore:
@Nullable
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
5.2 LiveData怎么做到页面重建时恢复数据的
如果是ViewModel保持的LivaData,在Activity的OnCreate中,我们调用了:
viewModel.mLiveData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.d("LiveData", integer.toString());
textView.setText(String.valueOf(integer));
}
});
注册了观察者,当Activity销毁重建时,状态变为active就会调用considerNotify,核心就在这个方法:
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}
首先判断Observer的状态,如果是active就继续,然后再判断一下是否应该是active,这一步应该是为了支持我们重写shouldBeActive方法时做一些自定义的判断,然后下面就是重点了。
实现这个功能的重点是什么,我觉得要区分LifeCycleOwner自然情况下从inactive变为active和异常情况下两种过程,比如activity 在执行onPause onStop了,然后又到了onResume,这个过程是自然的,并且如果没有发生数据变化是不应该去更新数据的,那这个version看着似乎就是为了区分这两种情况的。
当observer 的 mLastVersion >= 当前的version时就不会发出通知observer.mLastVersion只有在通知UI更新时才会调用,而且其值就是mVersion,所以重点就在于mVersion什么时候是>mLastVersion的,mVersion的赋值也只在一个地方:
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
当是销毁重建时Observer是一个新的实例,所以其mLastVersion为初始化的-1,而mVersion是LiveData的一个实例变量,没有被初始化,如果之前更新过数据,现在也一定会更新数据;当是正常流程的话mVersion和mLastVersion总是一致的,所以就调不到更新数据。
5.3 LiveData怎么做到页面由inactive变为active自动将数据更新的
这个疑问和第二个类似,当在inactive调用了setValue时,走到dispatchingValue,如果判断当时是inactive,就不会发出通知,当observer重新变为active时,由于mVersion ++ 没有同步给mLastVersion,所以就会发起通知,自动更新UI。
6. 缺点:
当Activity因为内存不足被意外销毁时,ViewModel的数据保存不了,为什么保存不了呢,因为在界面意外被销毁时就回调不了这个方法了onRetainNonConfigurationInstance,导致ViewModelStore没有被保存下来。所以我个人觉得,保存数据还是用onSaveInstance安全一点。
7. 总结
ViewModel + LivaData设计确实很巧妙,尤其是LiveData对数据自动更新的这种机制,还有很多细节这里没有提到,还是需要多多阅读源码,理解透彻。