一、ViewModel的作用
1、ViewModel
用于管理与界面(Activity、Fragment)相关的数据。
2、ViewModel让数据可在发生屏幕旋转等配置更改后仍能继续存在。
3、ViewModel 让Activity与Fragment共享数据更方便。
ViewModel作为JetPack中一个重要部件,本身并不复杂。ViewModel总结起来就一个功能:保存数据,并且在ViewModel中的数据,不会因为配置变化(横竖屏转换)而丢失,只有在Activity真正被销毁的时候,才会真正销毁数据。
二、ViewModel的使用
简单使用示例如下:
0、依赖添加。在build中添加如下依赖
def lifecycle_version = "2.2.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
1、定义。定义一个类MyViewModel继承ViewModel(抽象类)
public class MyViewModel extends ViewModel {
public int count;
}
2、简单使用。2.2.0版本后,获取ViewModelProvider的方式发生了变化,新版本取值方式如下:
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.count = 1;
三、原理说明
ViewModel为什么能在横竖屏变化后,仍能保存数据?ViewModel的生命周期为什么与Activity不同?
这里从三条线来说明:
一个是ViewModel如何存入到Activity中。
一个是ViewModel如何取出。
一个是Activity配置发生变化,ViewModel数据如何被保存下来的。
1、ViewModel的存入、取出(两条线并行)
以上面使用的例子为例,当定义好MyViewModel后,在界面上取值的流程如下:
//(1)get逻辑,取值入口
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
//(2)ViewModelProvider中的get方法
@MainThread
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);
}
//(3)真实将viewModel存入到mViewModelStore中。
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//从mViewModelStore中取值,首次为null
ViewModel viewModel = mViewModelStore.get(key);
//isInstance逻辑:当入参是null时,返回为false
//此处判断null与该类是否属于ViewModel
//当不为null,且该类属于ViewModel时,直接返回该viewModel
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.
}
}
//当viewModel为null的时候,新建viewModel对象
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
//将viewModel对象,存入到mViewModelStore中,供后续使用
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
通过以上三个步骤,完成获取当前viewModel值的首次新建、存入与取出的逻辑。
2、Activity配置发生变化,如何保存ViewModel数据
具体流程是这样的:
(1)从ActivityThread中RelaunchActivity开始:
android.app.ActivityThread#handleRelaunchActivity
--> handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents, pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
--> handleDestroyActivity(r.token, false, configChanges, true, reason);
--> performDestroyActivity(token, finishing, configChanges, getNonConfigInstance, reason);
--> activity.retainNonConfigurationInstances()
if (getNonConfigInstance) {
try {
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to retain activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
}
retainNonConfigurationInstances该方法位于activity中
--> android.app.Activity#retainNonConfigurationInstances
//(1)配置变化时,被调用
NonConfigurationInstances retainNonConfigurationInstances() {
//重点是这句,获取activity
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
mFragments.doLoaderStart();
mFragments.doLoaderStop(true);
ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
if (activity == null && children == null && fragments == null && loaders == null
&& mVoiceInteractor == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
//获取的activity存入nci中。
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}
//(2)onRetainNonConfigurationInstance在ComponentActivity中的具体实现
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
//此时获取的的这个nc,也就是上一次保存的NonConfigurationInstances中的activity(只是
//名称是activity,本质上是一个object)
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;
}
//(3)上一次保存的NonConfigurationInstances中的activity
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
将目光转回ActivityThread中,继续后续流程
--> android.app.ActivityThread#handleLaunchActivity
--> android.app.ActivityThread#performLaunchActivity
--> performLaunchActivity中调用activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback);
--> android.app.Activity#onCreate(android.os.Bundle) 在onCreate中重新赋值
至此,配置变化为何可以保存viewModel的流程已经完毕。
四、懂的更多
1、ViewModel中如何传入额外参数?
参考androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory。如需要额外传入参数,重写Factory。
再以本文前的MyViewModel为例,如果需要传入参数:
//(1)MyViewModel定义
public class MyViewModel extends ViewModel {
private int count;
public MyViewModel(int count){
this.count = count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
//(2)定义Factory
public class MyViewModelFactory implements ViewModelProvider.Factory {
private int count;
public MyViewModelFactory(int count){
this.count = count;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T)new MyViewModel(count);
}
}
//(3)传入参数与取值
MyViewModel myViewModel = new ViewModelProvider(this,new MyViewModelFactory(10)).get(MyViewModel.class);
2、ViewModel数据是在什么时候清理的?
生命周期走到destroy,且不是因为配置变化的时候,如下:
//ComponentActivity的构造函数中:androidx.activity.ComponentActivity#ComponentActivity()
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
//生命周期走到destroy
if (!isChangingConfigurations()) {
//并且不是因为配置变化,则清理数据
getViewModelStore().clear();
}
}
}
});
ViewModel在JetPack中,常与其他组件如LiveData等一起使用,共同构造了JetPack的生态。