[源码学习]ViewModel是什么?
ViewModel是Android架构组件包Jetpack的一个组件,它也是MVVM应用架构中的核心(Model-View-ViewModel),所以我们有必要学习一下ViewModel的基本原理
学习源码,我们还是老规矩,先看类结构图。
你没看错,ViewModel作为一个抽象类,只有一个空方法,
我们来看看这个类的介绍(Google developer官网介绍)
ViewModel类旨在以生命周期感知的方式来存储和管理UI相关的数据。ViewModel允许数据在配置更改的时候例如,屏幕旋转的情况下保存下来。
Android Framework层管理UI控制器activity和fragment的生命周期,由Framework可以在一定的用户行为或者设备事件超出你的控制以后,决定销毁或者重建一个UI控制器。
如果系统销毁或者重建一个UI控制器,所有与UI相关的临时数据都会消失,例如,在你的APP的其中一个Activity中有一个包含用户的列表,当这个activity重建或者配置更改的时候,新的activity不得不重新获取这个用户列表,对于简单的数据,activity可以使用onSaveInstanceState在onCreate()方法中恢复这些数据,但是这只适合少量可以序列化的数据,而不适合大量的数据例如用户列表或者图片。
另一个问题是,UI控制器经常需要进行异步调用,这可能需要一些时间才能返回。 UI控制器需要管理这些调用,并确保系统在销毁后将其清除,以避免潜在的内存泄漏。这种管理需要大量维护,并且在重新创建对象以进行配置更改的情况下,这浪费了资源,因为对象可能不得不重新发出已经发出的调用。
Doc介绍
ViewModel类负责准备和管理Activity和Fragment的数据,它同样处理Activity/Fragment与应用其他模块的通信。
ViewModel创建的时候总是需要一个Scope管理(fragment/activity)并且存活的时间与scope一样长,例如,如果scope是一个activity,那么直到它被finish
换句话说,这意味着viewModel它的所有者由于配置更改(例如旋转)而被销毁的时候,它也不会被销毁。
我们只需要关注重点就可以了
- 生命周期感知
- 用于保存数据
生命周期感知
什么叫做生命周期感知,官方文档这样说
生命周期感知组件通过一些操作来响应另外一些其他组件的生命周期变化,例如activity和fragment
首先我们来看一下ViewModel的正常使用方式,官方示例:
Activity:
public class UserActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.user_activity_layout);
//获得viewModel实例
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
//监听viewmodel的livedata数据
viewModel.userLiveData.observer(this, new Observer () {
@Override
public void onChanged(@Nullable User data) {
// update ui.
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.doAction();
}
});
}
}
ViewModel类:
public class UserModel extends ViewModel {
public final LiveData<User> userLiveData = new LiveData<>();
public UserModel() {
// trigger user load.
}
void doAction() {
// depending on the action, do necessary business logic calls and update the
// userLiveData.
}
}
这里有一个疑问,在activity中viewmodel对象如何被初始化的?另外这个对象不会在activity destory的时候回收吗?为什么可以在屏幕旋转的时候依然存在呢?一个个来
viewModel对象如何被创建
我们看到上面的代码
ViewModelProviders.of(this).get(UserModel.class);
传入了ViewModel对应的class,猜测可能是利用反射或者动态代理。
看一下ViewModelProviders和ViewModelProvider的类结构图
顺着上面的代码逻辑一步步学习
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
//对象工厂为null,则新建Factory
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
//获取一个androidViewModelFactory的单例
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
AndroidViewModelFactory继承自ViewModelProvider.NewInstanceFactory,ViewModelProvider.NewInstanceFactory实现了Factory接口关系类图如下:
这里我们可以重点看一下NewInstanceFactory的create方法
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
//反射得到对应model class的实例
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);
}
}
继续刚才的源码追查,我们已经知道viewModelProviders–>of方法,那么返回的是一个包含了viewModelFactory对象的viewmodelProvider的实例,那么of–>get(ViewModel::class)又是怎么样返回一个我们需要的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);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//1. 首先从mViewModel中尝试检索已经存在的对象(ViewModelStore内部维护了一个HashMap,key值默认是class name+DEFAULT_KEY)
ViewModel viewModel = mViewModelStore.get(key);
//2. 如果检索到的对象是我们需要的类型,直接返回
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//3. 如果不存在,则调用factory的create方法生成对应实例,并放入viewModelStore中
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
重点:
通过学习以上源码,我们大概解答了我们的第一个疑问,viewModel对象如何被创建的。答案就是
- 如果viewModelStore中之前存储过该对象,则直接返回
- 没有则使用AndroidViewModelFactory(或者自定义Factory)的create方法生成
为何在屏幕旋转的时候,viewModel对象怎么保存下来的?
我们在上面追踪如何生成viewModel对象的时候,其实在ViewModelProviders.of方法体构建ViewModelProvider对象时忽略了第一个参数,ViewModelStore,
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
在of方法中的调用如下:
new ViewModelProvider(activity.getViewModelStore(), factory);
继续查看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) {
//如果mViewModelStore为空,当我们的activity因为任何配置更改重建了,nc(NonConfigurationInstances)会包含上个实例中的viewStore,当我们的activity首次创建时nc==null
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
//如果是首次创建activity我们的nc为null,这个时候就需要创建一个mViewModelStore实例
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
让我们继续看一下NonConfigurationInstances什么时候会保存?
在tActivity由于配置更改而重建时,系统会回调onRetainNonConfigurationInstance方法,我们看一下FragmentActivity的这个方法如何实现的
@Override
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
if (fragments == null && mViewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
//将当前的viewModelStore保存下来
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;
}
在activity重建的时候,会重新调用attach方法,这里会将上次保存的NonConfigurationInstances重新拿到,因此我们的viewModelStore也可以保存下来了。
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken){
……
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
重点:
- viewModel对象是保存在ViewModelStore中的hashMap中
- 在activity由于旋转屏幕或者其他的配置更改重建时,会回调onRetainNonConfigurationInstance方法将当前的viewModelStore对象保存下来,并在新的activity的onattach方法回调的时候,重新获取
- 生命周期感知体现在activity onDestory的viewModelStore会调用clear方法
保存数据
参考上面的解释,保存数据是因为viewModleStore的保存机制。
参考网址
- https://developer.android.google.cn/topic/libraries/architecture/viewmodel
- https://blog.mindorks.com/android-viewmodels-under-the-hood