具有宿主生命周期感知能力的数据存储组件,只能感知宿主被销毁的事件,也就是onDestory,此时可以复写onClear方法来做一些清理和释放的工作
ViewModel保存的数据,在页面因配置变更导致页面销毁重建之后依然也是存在的
配置变更:
横竖屏切换;分辨率调整;语言切换;权限变更;系统字体样式变更等
原理:
ViewModel的实例被保存了下来,页面被重建之后,还是获取的同一个ViewModel的实例,就能实现里面数据的复用
正常关闭页面,ViewModel的数据还是会被清理的
如果是电量不足、内存不足等系统原因导致的页面被回收,页面重建之后存储的数据也能再次被复用。
存储的数据,仅仅只能当页面因为配置变更导致的销毁在重建时才可复用,复用的是ViewModel的实例化对象的整体
class TestViewModel : ViewModel() {
val liveData = MutableLiveData>()
fun loadData(): LiveData> {
//为了适配因配置变更而导致的页面重建,重复利用之前的数据,加快页面渲染,不再请求接口
if (liveData.value == null){
val remoteData = queryData()
liveData.postValue(remoteData)
}
return liveData
}
fun queryData():List{
//接口请求
……
return data
}
}
//通过ViewModelProvider来获取TestViewModel的实例
val viewModel = ViewModelProvider(this).get(TestViewModel::class.java)
viewModel.loadData().observe(this,Observer{
//数据回调
})
存储的数据,无论是配置变更,还是因内存、电量不足等系统原因导致的页面被回收再重建都可以复用,即便ViewModel不是同一个实例,它存储的数据也能做到复用,需要用到savedState组件
api 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.7.0'
//SavedStateHandle 包装了一个hashMap
class TestViewModel(val savedState: SavedStateHandle) : ViewModel() {
private val KEY_TEST_DATA = "key_test_data"
val liveData = MutableLiveData>()
fun loadData(): LiveData> {
//内存复用
if (liveData.value == null) {
val remoteData = savedState.get>(KEY_TEST_DATA)
liveData.postValue(remoteData)
return liveData
}
//请求接口
val remoteData = queryData()
savedState.set(KEY_TEST_DATA, remoteData)
liveData.postValue(remoteData)
return liveData
}
fun queryData():List{
//接口请求
……
return data
}
}
不仅能实现单Activity多Fragment的数据共享,还可以实现跨Activity的数据共享
ViewModel是如何做到在宿主销毁了,还能继续存在,以至于页面恢复重建后还能继续复用
因为获取的是同一个ViewModel实例对象
val viewModel = ViewModelProvider(this).get(TestViewModel::class.java)
ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory){
this(owner.getViewModelStore(), factory);
}
ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
在ViewModelProvider需要的是ViewModelStoreOwner,而Activity、Fragment都是实现了ViewModelStoreOwner这个接口的,所以可以直接传递进去
ViewModelStore就是用来存储ViewModel实例的,本质上是一个HashMap
Factory就是用来创建ViewModel实例的
调用get方法,将传递进来的className在拼接一个DEFAULT_KEY字符串,这个key就是ViewModel在ViewModelStore存储的key
@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);
}
首先根据key来获取ViewModel实例,紧接着看这个实例是不是Class类型,如果是直接返回了,如果不是就根据Factory的类型来创建ViewModel实例,创建完成之后就把它存储到mViewModelStore这个缓存对象中
@NonNull
@MainThread
public T get(@NonNull String key, @NonNull Class 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;
}
ViewModel的实例就是通过mViewModelStore来获取的,既然想做到ViewModel实例的复用,那就需要mViewModelStore做到复用,mViewModelStore的实例实在ComponentActivity的getViewModelStore()获取的
@NonNull
@Override
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.");
}
ensureViewModelStore();
return mViewModelStore;
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
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();
}
}
}
NonConfigurationInstances 本质是个包装类,用来包装一些在配置变更后还想继续留存的数据,比如fragment,页面被旋转之后,Activity会被重建,而Fragment还是原来的那个,这个实现原理就是通过NonConfigurationInstances来实现的,ViewModel的复用也是这样
mViewModelStore的存储是在onRetainNonConfigurationInstance中,如果ViewModelStore为空就直接retrun,不为空就构建了NonConfigurationInstances对象,并将ViewModelStore给提取了出来,从而完成实例对象的复用
@Override
@Nullable
@SuppressWarnings("deprecation")
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;
}
custom对象是在onRetainCustomNonConfigurationInstance()中得到的
作用:
如果想当配置变更导致被销毁了在Activity中保存一些数据,在Activity在重建时继续复用,此时可以复写onRetainCustomNonConfigurationInstance方法
通过调用getLastNonConfigurationInstance()方法来获取保存的方法
但是这种方式已经被废弃了,推荐使用ViewModel来完成
SavedState存储的数据即便ViewModel这个实例被销毁了,当Activity被重建的时候也能被复用
涉及到的类:
SavedState数据存储流程:
逐一调用每个SavedStateHandle保存自己的数据。汇总成一个总的Bundle,在存储到Activity的SavedState对象中