1.介绍
Activity
和Fragment
界面控制器是Android
系统提供给我们用的类,可能会根据当前的情况,随时销毁或者重新创建页面控制器,这样就需要我们对于当前界面的数据要做合理的保存,以便发生这种情况可以恢复,Activity
和Fragment
应该只做UI
数据的更新,页面数据的提供应该拆分给一个单独的类来做,就是ViewModel
。
2.使用
从整体看一下:
一个Activity
和Fragment
可以对应一个或者多个ViewModel
,用来提供数据
ViewModel
不应该绑定View
视图相关,Lifecycle
生命周期,Activity
,或者Context
这些Android
相关的类,数据改变通知UI
进行更新都需要使用LiveData
来做;
下图是ViewModel
的生命周期
3.源码分析
我们将测试APP运行之后,前三行的打印,然后再将屏幕进行旋转,再看一下相同的打印,从结果上我们可以看出来,只有ViewModel
是相同的,其他的Fragment
和Activity
都是新的对象。
这样我们就来分析一下,为什么需要这种方式来新建ViewModel
吧
以上就是整体的初始化过程,我们具体对照源码分析一下
我们来看一下是如何创建的ViewModel
mViewModel = new ViewModelProvider(this).get(MainViewModel.class);
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore()/*fragment 或者 activity会提供对应的ViewModelStore*/, owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
//第一次的情况下为null,如果旋转屏幕则不为空直接返回
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
requireActivity().getApplication(),
this,
getArguments());
}
return mDefaultFactory;
}
真正创建ViewModel
的地方是在SavedStateViewModelFactory
中的,那这个类干什么的呢?
{@link androidx.lifecycle.ViewModelProvider.Factory} that can create ViewModels accessing and
* contributing to a saved state via {@link SavedStateHandle} received in a constructor.
* If {@code defaultArgs} bundle was passed into the constructor, it will provide default
* values in {@code SavedStateHandle}.
通过查看注释我们可以知道它能创建一个包含SavedStateHandle
的构造函数,这样创建流程就结束了,我们看到就是将几个类进行了初始化,应该和配置没有关系,那么具体的实现就应该在get
方法里面,我们再接着来看:
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.使用了一个独一无二的名字,同创建的类名相关,ViewModelStore是一个hashmap,保存当前实例化的ViewModel类,这样就算是屏幕旋转也会保存在内存中了,只要在activity合适的时机上进行销毁即可。
ViewModel viewModel = mViewModelStore.get(key);
//2.第一次这个ViewModel为空,只有后续不为空的时候会走到这里面,执行SavedStateViewModelFactory中的onRequery,会将SavedStateHandleController将lifecycle进行关联(SavedStateHandleController使用来处理SavedStateHandle的)
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.
}
}
//自定义factory实现了此接口,我们看默认的就好了
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
//会在此进行创建,调用SavedStateViewModelFactory的create函数进行创建的
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
Constructor<T> constructor;
if (isAndroidViewModel) {
constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
} else {
//普通的ViewModel直接看这里,构造函数是否有SavedStateHandle,便于后续反射实例化
constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
}
// doesn't need SavedStateHandle
if (constructor == null) {
return mFactory.create(modelClass);
}
SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
try {
T viewmodel;
if (isAndroidViewModel) {
viewmodel = constructor.newInstance(mApplication, controller.getHandle());
} else {
//真正初始化了
viewmodel = constructor.newInstance(controller.getHandle());
}
viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
//返回viewModel
return viewmodel;
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to access " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("An exception happened in constructor of "
+ modelClass, e.getCause());
}
}
这样整理流程就梳理完毕了,我们知道了保存到了HashMap
中,只要一直有这个HashMap
那么就可以获取到ViewModel
的对象了,那我们接着来看一下屏幕发生旋转变化是由哪里触发的吧,我们知道旋转屏幕的话会将屏幕先destory
在create
,那我么看一下如果HashMap
同Activity
实例有关系按理说destory
的时候也会将HashMap
给清除啊,再看一下代码:
void destroy(@NonNull FragmentHostCallback<?> host,
@NonNull FragmentManagerViewModel nonConfig) {
if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(TAG, "movefrom CREATED: " + mFragment);
}
//false
boolean beingRemoved = mFragment.mRemoving && !mFragment.isInBackStack();
//true
boolean shouldDestroy = beingRemoved || nonConfig.shouldDestroy(mFragment);
if (shouldDestroy) {
boolean shouldClear;
if (host instanceof ViewModelStoreOwner) {
//如果是ViewModelStoreOwner那么为false
shouldClear = nonConfig.isCleared();
} else if (host.getContext() instanceof Activity) {
Activity activity = (Activity) host.getContext();
shouldClear = !activity.isChangingConfigurations();
} else {
shouldClear = true;
}
//旋转屏幕不会走到这里来
if (beingRemoved || shouldClear) {
如果走到这里的话最终会调用到ViewModeStore的clear方法的,
nonConfig.clearNonConfigState(mFragment);
}
mFragment.performDestroy();
mDispatcher.dispatchOnFragmentDestroyed(mFragment, false);
} else {
mFragment.mState = Fragment.ATTACHED;
}
}
这样我们就可以看到了,正常的Destory是会清除ViewModelStore
的,但是如果是屏幕旋转的时候做了特殊的处理所以还有保存的!