3.5 原理小课堂
在3.3节中,已经分析了ViewModelProvider(this).get的内部方法,我们从中了解到创建的ViewModel对象都会被存在ViewModelStore中,如果创建的ViewModel对象已存在,则直接取出对象并返回,如果不存在则新建。
ViewModelProvider(this)中this的参数类型是ViewModelStoreOwner,由于在androidx.activity.ComponentActivity中实现了ViewModelStoreOwner接口并实现了getViewModelStore方法,因此开发者可以直接使用当前Activity的上下文this,主要代码如下:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements ViewModelStoreOwner{ ... @NonNull @Override 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."); } ensureViewModelStore(); return mViewModelStore; } ... }
接着来看getViewModelStore方法,此方法会进入ensureViewModelStore方法中,ensureViewModelStore方法的代码如下:
void ensureViewModelStore() { if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } }
这里通过(NonConfigurationInstances) getLastNonConfigurationInstance()方法来获取上一次的配置信息,NonConfigurationInstances中有viewModelStore对象,代码如下:
static final class NonConfigurationInstances { Object custom; ViewModelStore viewModelStore; }
如果上一次配置信息不为空,就直接使用上一次的viewModelStore,如果为空则新建viewModelStore。在Activity旋转屏幕被销毁的时候,不仅会调用onSaveInstance-State方法,而且会调用onRetainNonConfigurationInstance方法,onRetainNonConfiguration-Instance方法的代码如下:
public final Object onRetainNonConfigurationInstance() { Object custom = onRetainCustomNonConfigurationInstance(); ViewModelStore viewModelStore = mViewModelStore; if (viewModelStore == null) { 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; }
可以看到,在onRetainNonConfigurationInstance方法中对viewModelStore进行了存储。当屏幕旋转恢复的时候会通过getLastNonConfigurationInstance方法进行恢复。getLastNonConfigurationInstance方法的代码如下:
@Nullable public Object getLastNonConfigurationInstance() { return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null; }
所以在Activity旋转屏幕的整个过程中,ViewModelStore对象保留了下来,通过ViewModelProvider(this).get方法获取到的是同一个ViewModel实例,从而避免了由于屏幕旋转而导致数据丢失的问题。
前面也提到了ViewModel虽然可以防止屏幕旋转引起的数据丢失,但ViewModel并不能代替onSaveInstanceState方法,主要原因有如下两点:
- onSaveInstanceState方法可以存储少量的序列化数据,ViewModel可以存储任意数据,只是使用时的限制不同。
- onSaveInstanceState可以达到数据持久化的目的,但是ViewModel不可以,使用场景不同。
为什么说ViewModel不能达到数据持久化的目的呢?因为当Activity被真正销毁的时候,ViewModel会将资源进行回收,示例代码如下:
getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { ... if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } });
从上面代码可以看出,当对应的Activity被真正销毁,即不是屏幕旋转导致页面被销毁时,viewModelStore将会调用clear方法清理数据,所以ViewModel并不能达到数据持久化的目的。
可见,ViewModel并不能替代onSaveInstanceState方法,在实际开发中,读者应选择适合自己业务的方法,从而达到最佳体验。