注意:本文ViewModel相关使用及源码均是2.2.0版本
基本使用
详细使用请阅读Jetpack全家桶(第3篇)之ViewModel使用篇
class MyViewModel : ViewModel() {
val users = MutableLiveData<List<User>>()
//省略
````
}
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// val model = ViewModelProvider(this).get(MyViewModel::class.java)
val model = ViewModelProvider(this)[MyViewModel::class.java]
model.users.observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}
以前通过ViewModelProviders.of方法来获取ViewModel已经过时了,现在我们是通过ViewModelProvider方法创建ViewModel对象。
首先,我们先来看ViewModelProivder的构造方法。ViewModelProivder有很多构造方法,不过最终都调到同一个地方:
ViewModelProivder的构造方法
/**
* Creates {@code ViewModelProvider}. This will create {@code ViewModels}
* and retain them in a store of the given {@code ViewModelStoreOwner}.
* <p>
* This method will use the
* {@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory() default factory}
* if the owner implements {@link HasDefaultViewModelProviderFactory}. Otherwise, a
* {@link NewInstanceFactory} will be used.
*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
*
* @param owner a {@code ViewModelStoreOwner} whose {@link ViewModelStore} will be used to
* retain {@code ViewModels}
* @param factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
/**
* Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
* {@code Factory} and retain them in the given {@code store}.
*
* @param store {@code ViewModelStore} where ViewModels will be stored.
* @param factory factory a {@code Factory} which will be used to instantiate
* new {@code ViewModels}
*/
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
第1种和第2种构造方法中的this调用的是第3种构造方法,最终都调到同一个地方
//第3种构造方法
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
正常业务我们使用第一种构造方法就够了,第二种构造方法多了一个Factory,那么加入Factory对象之后,相比较于以前有什么好处呢?
加了Factory之后,我们可以定义构造方法带参的ViewModel。比如说,如果,我们的一个ViewModel构造方法需要带一个id参数,那么我们可以在Factory的create方法里面创建对象直接带进去。
//第2种构造方法
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
//使用
ViewModelProvider(this, object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>) =
// MyViewModel(id) as T
MyViewModel() as T
})
切忌,我们不要自己创建ViewModel对象,因为自己创建的对象不能保存因为配置更改导致Activity重建的数据,从而完美避开了ViewModel的优点。
针对于ViewModel的源码分析,本文从Factory这个切入点入手,逐步的分析ViewModel的创建和恢复。
ViewModel的源码
Factory就是我们预期的工厂类,用来创建ViewModel对象;
ViewModelStoreOwner从类图上来看,我们熟悉的ComponentActivity和Fragment实现了这个接口(androidX包下)
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
//省略
````
}
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
ViewModelStoreOwner, SavedStateRegistryOwner {
//省略
````
}
/**
* A scope that owns {@link ViewModelStore}.
* <p>
* A responsibility of an implementation of this interface is to retain owned ViewModelStore
* during the configuration changes and call {@link ViewModelStore#clear()}, when this scope is
* going to be destroyed.
*/
@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
owner.getViewModelStore()得到了ViewModelStore对象,ViewModelStore是一个什么东西呢?这个很好理解,ViewModelStore就是用来存储的ViewModel对象的,比如同一个Activity的onCreate方法可能会多次回调,我们在onCreate方法初始化ViewModel的,但是不可能每次onCreate回调都会创建新的ViewModel对象,所以需要有一个东西用来存储的我们之前创建过的ViewModel,这个就是ViewModelStore的作用。
我们再来看一下ViewModelProvider(this).get(MyViewModel::class.java)中的get方法,因为真正获取ViewModel对象就是通过这个方法的。
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
这个get方法没有做什么事情,构造了一个默认的key,然后调用另一个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;
}
这个get方法总的来说,主要分为以下2个过程:
先通过key从ViewModelStore(缓存)获取ViewModel对象,如果缓存中存在,直接返回。Activity经过横屏重建之后,返回ViewMode的对象l就是这里返回。如果缓存不存在,那么通过Factory创建一个对象,然后放在缓存中,最后返回。
这里的核心是OnRequeryFactory,我们发现,当ViewModel是从ViewModelStore获取的,就是回调一次OnRequeryFactory的onRequery方法。这里回调onRequery方法有什么作用呢?这个设计到后面的SavedStateHandle,我们都知道当Activity因为资源限制被系统杀掉之后重新创建会恢复之前的状态,这里的onRequery方法的作用就是--为在这中情况能保存数据做做准备(往SavedStateRegistry里面注册SavedStateProvider)。那么可能有人就要问了ViewModel没有从缓存获取ViewModel,为啥不回调onRequery方法呢?我想说的是,注册最后都是要做的是,只是做工作可能不太一样,所以看不到onRequery方法的回调。这里,我先粗略描述一下,后面的内容会详细的分析这一块的内容,大家先对此有一个印象就行。
ViewModel如何保存数据
我们都知道ViewModel是从一个ViewModelStore缓存里面的获取,我们看了ViewModelStore的源码,发现它的内部并没有通过静态缓存实现。那么它是怎么实现Activity在onDestroy之后(重建),还继续保留已有的对象呢?
这个我们可以从ComponentActivity的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;
}
getViewModeStrore方法的目的很简单,就是获取一个ViewModelStrore对象。那么这个ViewModelStore可以从哪里获取呢?我们从上面的代码中可以找到两个地方:
- 从NonConfigurationInstances获取。
- 创建一个新的ViewModelStore对象。
第二点我们不用看,关键是NonConfigurationInstances。NonConfigurationInstances这是什么东西?这里,我简单解释一下NonConfigurationInstances:
在Activity实例中重写onRetainCustomNonConfigurationInstance、getLastNonConfigurationInstance方法,打印数据,当横竖屏旋转屏幕时,会打印这两个方法,一个是activity销毁时调用,一个是activity重建时调用。
override fun onRetainCustomNonConfigurationInstance(): Any? {
Log.i("minfo", "onRetainCustomNonConfigurationInstance")
return super.onRetainCustomNonConfigurationInstance()
}
override fun getLastNonConfigurationInstance(): Any? {
Log.i("minfo", "getLastNonConfigurationInstance")
return super.getLastNonConfigurationInstance()
}
ComponentActivity
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
@Override
@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;
}
public Object onRetainCustomNonConfigurationInstance() {
return null;
}
Activity类中方法:
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
该方法会创建NonConfigurationInstances对象,将已有的viewModelStore存入NonConfigurationInstances。
然后调用getLastNonConfigurationInstance,获取activity中的mLastNonConfigurationInstances实例,其中保存着viewModelStore。
同时,我们还可以在ComponentActivity里面看到一段代码:
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非配置改变(比如点击返回finish按钮)销毁的话,则不会保存之前创建的ViewModel对象,对应的是ViewModelStore的clear方法调用。 如果配置改变,不做操作,并不会清空ViewModelStore里面的内容.
总结
- ViewModel 和 onSaveInstaceState方法区别在于:ViewModel只能保存因为配置更改导致重建的数据,但是它能保存大量和复杂的数据;onSaveInstaceState能保存配置更改导致重建和资源限制导致重建的数据,但是它只能保存少量简单的数据。ViewModel使用SavedStateHandle能够保存资源限制导致重建的数据。
- ViewModel的生命周期之所以比Activity的生命周期生命周期,主要重建之后的Activity用的是之前的ViewStore。ViewModelStore保存的是之前的ViewModel,而ViewStore在配置更改导致重建不会清空已有ViewModel。