简介
ViewModel是JetPack生命周期管理库中的一个组件。它可以提供并且管理数据,可感知生命周期,同时不会随着配置(eg:屏幕旋转导致的Activity重新创建)变更而改变。 使用它可以方便的将UI界面和数据逻辑剥离开来,从而达到 UI只负责显示数据和处理用户操作,ViewModel负责提供、管理数据以及通讯。 典型使用场景:
- 作为数据持有和管理者(eg:处理网络请求);
- Fragment之间的通信;
- ViewModel代替加载器(eg:CursorLoader)。
生命周期
官方对其生命周期的描述如下:
ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 activity,是在 activity 完成时;而对于 fragment,是在 fragment 分离时。
生命周期概括起来就是:从Activity第一次创建ViewModel,直至Activity销毁,ViewModel都一直存在。
基本使用
定义ViewModel:
class CustomModel() : ViewModel() {
var data = 0
}
初始化和使用:
val customModel = ViewModelProvider(this).get(CustomModel::class.java)
customModel.data = 100;
findViewById<TextView>(R.id.tv).text = "${customModel.data}"
注意:务必不要直接(使用New)创建ViewModel,因为ViewModelProvider是确保ViewModel实例唯一性(同Activity内)和生命周期的关键。
那么如何创建需要初始化参数的ViewModel呢?答案是使用ViewModelProvider.Factory
,一个工厂模式:
class CustomModel(data: Int) : ViewModel() {
var data = 0
init {
this.data = data
}
}
class CustomModelFactory(data: Int) : ViewModelProvider.Factory {
var data = 0
init {
this.data = data
}
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return CustomModel(data) as T
}
}
使用:
val customModel = ViewModelProvider(this, CustomModelFactory(1000)).get(CustomModel::class.java)
findViewById<TextView>(R.id.tv).text = "${customModel.data}"
源码分析
接下来我们从ViewModel的源码进行分析:
ViewModel的创建
上文中我们说过,必须使用ViewModelProvider去创建ViewModel。这是确保ViewModel实例唯一性和生命周期的关键。那么我们就看一看ViewModelProvider是如何创建ViewModel的。
ViewModelProvider提供了三个构造函数:
- ViewModelProvider(ViewModelStoreOwner);
- ViewModelProvider(ViewModelStoreOwner, Factory);
- ViewModelProvider(ViewModelStore, Factory);
在代码中最终都是通过ViewModelProvider(ViewModelStore, Factory)
创建实例:
///构造方法一
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
///构造方法二
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
///构造方法三
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
可以看到:构造方法一和二都是通过调用改造方法实现的。
构造方法一:首先获取ViewModelStore,然后判断ViewModelStoreOwner是否有创建ViewModel的工厂方法,有则直接使用(下文详细分析)。没有就是使用NewInstanceFactory。 构造方法二和一类似,也是首先获取ViewModelStore,只不过是多了个Factory参数。
ViewModelStoreOwner和ViewModelStoreOwner
我们先看ViewModelStoreOwner和ViewModelStoreOwner。
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
源码很简单:ViewModelStoreOwner
就是一个接口,而ViewModelStore
则有了一些内容:它通过一个HashMap持有并管理者ViewModel,并且提供了增加和获取ViewModel的方法,以及清空HashMap和所有ViewModel的方法。结合它们名字中的Store。
我们不难推测:
ViewModelStoreOwner
就是ViewModelStore
持有者;ViewModelStore
是用来存储ViewModel的。
然后根据上文中创建ViewModel的代码:ViewModelProvider(this).get(CustomModel::class.java)
。可以肯定的是,我们的Activity一定实现了ViewModelStoreOwner
接口。
回看我们的Activity,它的继承关系如下图:
最终,可以在ComponentActivity
里看到它实现了ViewModelStoreOwner
接口。并且,还实现了HasDefaultViewModelProviderFactory
接口。
///...
private ViewModelStore mViewModelStore;
///...
public ViewModelStore getViewModelStore() {
///...
ensureViewModelStore();
return mViewModelStore;
}
///...
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
上面的代码就是从ComponentActivity
提取的关键代码,它涵盖ViewModel的创建和获取,并且确保了ViewModelStore实例的唯一性。我们的Activity通过继承即可实现对ViewModelStore的持有和管理,并间接控制所有ViewModel。
ViewModel实例的获取
在上文中讲的ViewModelStoreOwner和ViewModelStore都只是前置任务,真正的创建在get
方法中:
ViewModelProvider.get(Class modelClas)的代码如下:
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);
}
@NonNull
@MainThread
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;
}
代码中有关键的三步:
- 第一步:根据类名创建Key值;
- 第二步:检查ViewModelStore里是否有当前类型的ViewModel的实例,有就直接返回实例;
- 第三步:如果ViewModelStore没有当前类型的实例,那么创建新的实例,将其加入到ViewModelStore中并返回实例对象。
可以看到,ViewModel的创建就是在第三步完成的。 同时在这里,上文中的ViewModelStore
开始发挥作用了。正是通过ViewModelStore
,我们的Activity才实现了对ViewModel的持有,同时,使用一个HaspMap的数据结构,通过类名做Key值,确保了同一类型的ViewModel的唯一性。也就是:在同一个Activity内,同一类型的ViewModel实例只会被实例化一次,即:只会有一个实例,在Fragment中也是如此。
Factory和真正的创建
在上一小节中,我们发现:ViewModel的实例是通过Factory创建的。Factoty是ViewModelProvider中的一个接口:
public interface Factory {
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
代码很简单,只有一个create方法。也就是ViewModel创建的地方。这个有点类似抽象工厂模式啊。 在开篇的基本使用中,我们讲过点构造参数的ViewModel的初始化。那里我们直接实现了一个Factory又来创建实例。这里不再赘述。我们只看一下ViewModelProvider(ViewModelStoreOwner)
函数(也就是上文中ViewModelProvider的构造方法一)获取实例中的Factory从何而来:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
Factory的来源有两个: 如果ViewModelStoreOwner没有实现HasDefaultViewModelProviderFactory
接口,则使用NewInstanceFactory
。其代码如下:
public static class NewInstanceFactory implements Factory {
private static NewInstanceFactory sInstance;
@NonNull
static NewInstanceFactory getInstance() {
if (sInstance == null) {
sInstance = new NewInstanceFactory();
}
return sInstance;
}
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
代码没什么复杂的:
- 采用静态单例模式确保单例;
- 实现了Factory接口,在create方法中通过反射获取Viewmodel实例
接下来看一下如果ViewModelStoreOwner实现HasDefaultViewModelProviderFactory
接口的情况: HasDefaultViewModelProviderFactory源码很简单,只顶一个了一个获取Factory实例的方法:
public interface HasDefaultViewModelProviderFactory {
@NonNull
ViewModelProvider.Factory getDefaultViewModelProviderFactory();
}
HasDefaultViewModelProviderFactory有众多实现,我们只看一下ComponentActivity
中的实现作为参考:
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
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 (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
可以看到,里面也是创建了一个SavedStateViewModelFactory
,其中SavedStateViewModelFactory
的父类实现了Factory
接口,具体实现这里不再叙述了。
ViewModel的销毁
上文中详细讲述了ViewModel的创建,以及ViewModel是如何与Activity产生关系的。接下来,我们看下ViewModel是如何被销毁的。这里涉及到Lifecycle
的知识点,不展开详细讲述了。 还记得上文中我们的Activity的继承关系吗?它的父类中有个FragmentActivity
。在FragmentActivity
中的onDestroy
方法,向一个LifecycleRegistry
发送了当前Activity的生命周期。代码如下:
@Override
protected void onDestroy() {
super.onDestroy();
mFragments.dispatchDestroy();
mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
}
继续追踪LifecycleRegistry
,其关键代码如下:
public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
moveToState(event.getTargetState());
}
private void moveToState(State next) {
sync();
}
private void sync() {
while (!isSynced()) {
if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
backwardPass(lifecycleOwner);
};
if (!mNewEventOccurred && newest != null
&& mState.compareTo(newest.getValue().mState) > 0) {
forwardPass(lifecycleOwner);
}
}
mNewEventOccurred = false;
}
private void forwardPass(LifecycleOwner lifecycleOwner) {
observer.dispatchEvent(lifecycleOwner, event);
}
private void backwardPass(LifecycleOwner lifecycleOwner) {
observer.dispatchEvent(lifecycleOwner, event);
}
void dispatchEvent(LifecycleOwner owner, Event event) {
State newState = event.getTargetState();
mState = min(mState, newState);
mLifecycleObserver.onStateChanged(owner, event);
mState = newState;
}
省去其中的各种细节,FragmentActivity
通知的LifecycleRegistry
生命周期发生变化,LifecycleRegistry
会低啊用LifecycleEventObserver
的onStateChanged
方法。而LifecycleEventObserver
是一个接口,仅有 onStateChanged
一个方法。在ComponentActivity
的构造方法中,使用匿名内部类的方式实现了这个接口:
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
mContextAwareHelper.clearAvailableContext();
// And clear the ViewModelStore
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
看到这个实现,销毁方法付出水面。正对应我们上文中所说的ViewModelStore
的clear方法。
不难发现:ViewModel
的生命周期是通过Lifecycle
与Activity绑定的。每当activity销毁时,onDestory方法就会通过Lifecycle调用ViewModelStore的clear方法,从而达到销毁ViewModel的目的。
总结
本文主要讲解了ViewModel的相关源码和基本使用。着重介绍了它的创建、销毁以及和Activity生命周期的绑定。其实ViewModel本身的源码很简单。在库中只有几个文件:
而且每个文件最多不过寥寥百行代码。让ViewModel发挥出它真正的威力,离不开JetPack其他组件的配合和支持。文中的Lifecycle就是一个典型的例子。配合LiveData、Room以及Kotlin的协程,能大幅提升开发的销量,提升你的代码质量。