1、什么是ViewModel
ViewModel 具备宿主生命后期感知能力的数据存储组件,可以理解为ViewModel可以用来存储数据,而且在Activity因为异常销毁重新创建,依旧存在。这个和Activity的onInstanceSave和onRestoreInstance有点相似。
ViewModel的数据是可以再Activity和Fragment中共享的。
2、ViewModel简单使用
导入依赖
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
对于ViewModel,刚开始学习先去学习它的几个特性即可。
1、可以在Activity,Fragment中共享数据。
2、可以在多个Activity中共享数据。
3、在异常的生命周期时依旧可以保存数据。
代码中获取ViewModel
// 1、初始化ViewModel
var myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
// 2、初始化ViewModel
var myViewModel = ViewModelProvider(viewModelStore,ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
以上两种方式都可以。初始化之后即可使用ViewModel中的数据了。
在Activity和Fragment中共享数据的不同初始化方法
在Activity中使用:
var myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
this代表activity或者application,如果是Application,那就代表这个viewmodel能在多个Activity中共享了。
ViewModel使用起来比较简单,示例代码我写在最后。一般和LiveData联用。
3、ViewModel的源码分析
我们从获取ViewModel的get方法说起。
@MainThread
public T get(@NonNull String key, @NonNull Class modelClass) {
// mViewModelStore 可以理解为一个保存VieModel的map,
// 通过key去获取这个ViewMode
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
// 如果获取到了这个ViewModel,那就直接返回
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
// 没有获取到这个ViewModel,那就通过key去生成一个新的ViewModel
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
通过这段代码,我们需要知道
1、key如何定义;
2、viewmodel如何创建的;
@NonNull
@MainThread
public T get(@NonNull Class 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);
}
可以看到这个方法,
1、会有一个DEFAULT_KEY+ ViewModle类的类名。
2、直接调用get(String key,Class
我们再看下如何创建这个ViewModel:
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
*
*
* @param modelClass a {@code Class} whose instance is requested
* @param The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
T create(@NonNull Class modelClass);
}
在get方法中可以看到这个create方法。他是在一个Factory的接口,从这个名字可以看到,这个实际上是一个工厂模式。
实现类有这么多,那到底使用哪个呢?
再回到初始化ViewModelProvider这个方法
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
可以看到,这里会默认生成几个Factory。源码中包含两个Factory的实现。NewInstanceFactory和AndroidViewModelFactory,可以简单看下NewInstanceFactory
@Override
public T create(@NonNull Class 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);
}
}
既然是工厂模式,那就都有不同的create方法实现,NewInstanceFactory就是直接通过class.newInstance的方法去实例化对象。
初步了解初始化之后,我们开始解决疑问:
1、这个东西为啥能在Activity和Fragment中共享数据?
初始化ViewModelProvider的方法。
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
实际上调用的是:
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
mViewModelStore 这个东西是从Activity中传过来的。所以在Activity中是唯一的。
前面的get方法也可以看到,一旦这个ViewModel创建出来就会被放到mViewModelStore中去保存,下一次可以直接从mViewModelStore中拿出来。那么如果Fragment需要共享Activity中的数据,只需要在初始化的时候传入Activity和相同的ViewModel的class即可。
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
var viewModel = ViewModelProvider(requireActivity()).get(MyViewModel::class.java)
}
这样,拿到的mViewModelStore是Activity的,传入的MyViewModel::class.java也会从mViewModelStore拿到已经存在的View Model,自然就可以实现数据的共享了。
2、这个东西为啥能在多个Activity中共享数据?
如果不看源码,就可以直接理解为,这个ViewModel就是和Application的生命周期同步的,所有的Activity都能获取到这个对象,自然也可以共享里面的数据。
1、使用AndroidViewModelFactory。如果使用AndroidViewModelFactory就可以在多个Activity中共享数据。
2、Application实现ViewModelStoreOwner,也可以做到在多个Activity中实现共享数据。
class BaseApplicaiton : Application(), ViewModelStoreOwner {
companion object{
var instance:BaseApplicaiton? = null
}
private val appViewModelStore: ViewModelStore by lazy {
ViewModelStore()
}
override fun onCreate() {
super.onCreate()
instance = this
}
override fun getViewModelStore(): ViewModelStore {
return appViewModelStore
}
}
3、ViewModel是如何在Activity异常生命周期恢复数据的?
保存数据可以看下ActivityThread的performDestoryActivity方法:
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
Class extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
if (r != null) {
activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}
performPauseActivityIfNeeded(r, "destroy");
if (!r.stopped) {
callActivityOnStop(r, false /* saveState */, "destroy");
}
// 具体看这段代码
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);
}
}
}
// 省略若干代码
return r;
}
retainNonConfigurationInstances这个方法就是将Activity的一些参数配置保存到NonConfigurationInstances 中。
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
// 省略若干代码
return nci;
}
onRetainNonConfigurationInstance:这里主要就是将Activity的viewModelStore保存到NonConfigurationInstances。
public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
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;
}
继续追溯源码getLastNonConfigurationInstance方法可以看到保存数据的NonConfigurationInstances.
/* package */ NonConfigurationInstances mLastNonConfigurationInstances;
static final class NonConfigurationInstances {
Object activity;
HashMap children;
FragmentManagerNonConfig fragments;
ArrayMap loaders;
VoiceInteractor voiceInteractor;
}
Activity中有个ActivityClientRecord,可以理解为记录Activity的的一些参数集合。里面就有 Activity.NonConfigurationInstances lastNonConfigurationInstances;从上面的代码中可以看到,当Activity销毁的时候这里就是将Activity的NonConfigurationInstances赋值给了ActivityClientRecord的NonConfigurationInstances。
如何还原数据可以看下ActivityThread的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,
r.assistToken);
这里会将lastNonConfigurationInstances传入,这里调用了activity的attach方法。通过这个方法将ActivityClientRecord的NonConfigurationInstances赋值给了Activity的NonConfigurationInstances。
4、ViewModel和onSaveInstance()的区别
1、onSaveInstance是通过Bundle保存数据的,保存可序列化数据,一般大小为1M-8k。
ViewModel可以保存任何形式的数据,大小不限制,不超过系统的App分配的内存即可。
2、onSaveInstance是将数据保存的磁盘中,而ViewModel是将数据保存在内存中。
5、总结
掌握ViewModel需要了解
1、ViewModel的基本用法。
2、ViewModel的数据共享原理。
3、ViewModel是如何做到Activity异常生命周期还能保存数据的。
4、ViewModel和onSaveInstance的一些区别。