JectPack组件中,ViewModel主要用来封装与界面相关的数据,同样ViewModel是具备生命周期感知能力,在Activity销毁之前,或者Fragment onDetach 之前,是一直存在内存中;尤其是在屏幕旋转等系统配置更改后,ViewModel保存的界面数据依然存在
ViewModel源码分析
1 ViewModel的创建
ViewModel的创建,如果直接new出来一个对象也是可以的,但是官方提供的方式是通过ViewModelProvider + get的方式获取,这种方式在Kotlin中中,被封装成了viewModels和activityViewModels的语法糖,通过这两种方式创建ViewModel
@MainThread
public inline fun <reified VM : ViewModel> Fragment.viewModels(
noinline ownerProducer: () -> ViewModelStoreOwner = { this },
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)
这里就拿viewModels来说,这是一个内联高级函数,在Fragment的onCreateView方法中调用时,是直接完成ViewModel的创建
val viewModel = viewModels<MyViewModel>()
viewModels存在两个参数,ownerProducer,返回是一个ViewModelStoreOwner,默认是this,就是调用该函数当前的Fragment;factoryProducer,创建ViewModel的工厂,可以为空不传
如果想要使用自己的工厂去创建ViewModel,需要实现ViewModelProvider.Factory接口
class ViewModelFactory : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return modelClass.getConstructor(MyViewModel::class.java).newInstance()
}
}
使用方式如下
val viewModel = viewModels<MyViewModel>{
ViewModelFactory()
}
另外一种方式activityViewModels创建ViewModel,需要一个上下文,其他的和viewModels一致,两者的区别后续会讲解。
2 ViewModel源码分析
不管是viewModels还是activityViewModels,都会走到createViewModelLazy方法,
@MainThread
public fun <VM : ViewModel> Fragment.createViewModelLazy(
viewModelClass: KClass<VM>,
storeProducer: () -> ViewModelStore,
factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}
对于viewModels,如果传入的factoryProducer为空,那么就会个一个默认的ViewModelProviderFactory
Fragment # getDefaultViewModelProviderFactory
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (mDefaultFactory == null) {
Application application = null;
Context appContext = requireContext().getApplicationContext();
while (appContext instanceof ContextWrapper) {
if (appContext instanceof Application) {
application = (Application) appContext;
break;
}
appContext = ((ContextWrapper) appContext).getBaseContext();
}
if (application == null && FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(FragmentManager.TAG, "Could not find Application instance from "
+ "Context " + requireContext().getApplicationContext() + ", you will "
+ "not be able to use AndroidViewModel with the default "
+ "ViewModelProvider.Factory");
}
mDefaultFactory = new SavedStateViewModelFactory(
application,
this,
getArguments());
}
return mDefaultFactory;
}
最终返回一个SavedStateViewModelFactory,这是默认的工厂,这里会判断上下文application是否为空,如果为空,那么就mFactory = NewInstanceFactory;否则就是AndroidViewModelFactory,在activityViewModels调用时,就会传入一个上下文,源码可自己去看
public SavedStateViewModelFactory(@Nullable Application application,
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs) {
mSavedStateRegistry = owner.getSavedStateRegistry();
mLifecycle = owner.getLifecycle();
mDefaultArgs = defaultArgs;
mApplication = application;
mFactory = application != null
? ViewModelProvider.AndroidViewModelFactory.getInstance(application)
: ViewModelProvider.NewInstanceFactory.getInstance();
}
最终获取ViewModel,就是在ViewModelLazy方法
ViewModelProvider # ViewModelLazy
public class ViewModelLazy<VM : ViewModel> (
private val viewModelClass: KClass<VM>,
private val storeProducer: () -> ViewModelStore,
private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {
private var cached: VM? = null
override val value: VM
get() {
val viewModel = cached
return if (viewModel == null) {
val factory = factoryProducer()
val store = storeProducer()
ViewModelProvider(store, factory).get(viewModelClass.java).also {
cached = it
}
} else {
viewModel
}
}
override fun isInitialized(): Boolean = cached != null
}
首先是从缓存里拿ViewModel,如果缓存中不存在ViewModel,那么就通过ViewModelProvider + get去拿,这里主要看get方法
ViewModelProvider # get
@NonNull
@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);
}
这里是拿到了类的全类名和DEFAULT_KEY拼接成一个key,去mViewModelStore里面取,mViewModelStore就是在viewModels里默认的this,每个Activity和Fragment都有一个mViewModelStore
Activity和Fragment都实现了ViewModelStoreOwner接口,这个接口中存在一个方法
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
在调用createViewModelLazy时,其实已经传入了ViewModelStore,ViewModelStore其实就是一个HashMap
ComponentActiivty # 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.");
}
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();
}
}
}
通过拼接好的key,去mViewModelStore中获取ViewModel,如果第一次没有创建过ViewModel,肯定获取不到,那么走到1的位置
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
//2 第二次进来走这个位置
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.
}
}
//1 第一个进来走这个位置
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
} else {
viewModel = mFactory.create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
这里会判断传入的工厂类型,一般情况下,会走else,走我们自己创建的工程的create方法
ViewModelProvider # Factory – create
public interface Factory {
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
这样就创建了一个ViewModel,然就保存到mViewModelStore中;
所以viewModels在创建ViewModel时,先是从缓存中拿,如果缓存中没有,就从mViewModelStore中获取,如果mViewModelStore也没有,那么就重新创建,相当于一个三级缓存,是一个单例模式。
3 ViewModel存储数据
当屏幕旋转或者其他配置改变的时候,Activity会被销毁重建,页面数据的保存,通常在页面销毁时调用onSaveInstanceState,重建时调用onRestoreInstanceState,这种只能保存轻量级的数据(因为Bundle),所以ViewModel的出现,就解决了传统方式不能存储大量数据的问题
在activity中,存在两个非生命周期方法,onRetainNonConfigurationInstance和getLastNonConfigurationInstance
ComponentActivity # onRetainNonConfigurationInstance
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;
}
}
//如果viewModelStore为空,
//或者自定义的状态为空,没有自定义保存状态,就是不需要保存
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
在屏幕旋转时,会调用onRetainNonConfigurationInstance,当页面准备销毁时,调用onRetainCustomNonConfigurationInstance,这个方法可以重写,自定义页面数据的存储状态,这个时候,拿到mViewModelStore,这个时候还没有从内存清除,就创建NonConfigurationInstances,将viewModelStore以及数据保存状态保存到NonConfigurationInstances中
Activity # retainNonConfigurationInstances
NonConfigurationInstances retainNonConfigurationInstances() {
// 调用 ComponentActivity中的onRetainNonConfigurationInstance
// activity中保存的就是viewModel和数据存储状态
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();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}
最终,Activity销毁之前,将Activity相关的信息,全部保存在了NonConfigurationInstances中
当Activity销毁之后,重新启动后,在attach方法中,传入了lastNonConfigurationInstances,就是页面销毁之前保存的状态信息
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,
IBinder shareableActivityToken)
这里具体的怎么传递的,加一个TODO,之后在AMS中,会着重看一下源码,这样,在Activity重新启动之后,再次去getViewModelStore,会调用getLastNonConfigurationInstance
Activity # getLastNonConfigurationInstance
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
这里mLastNonConfigurationInstances已经不为空(attach中传入),返回 mLastNonConfigurationInstances.activity,这个activity中就保存了viewModelStore以及页面保存的数据状态,实现了页面数据的保存
至于页面在销毁后,ViewModel是怎么销毁的,就是LifeCycle的功劳了
ComponentActivity的构造方法
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
mContextAwareHelper.clearAvailableContext();
// And clear the ViewModelStore
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
当监听到Activity的ON_DESTROY后,就将ViewModelStore里的全部ViewModel都清空,当然是保存数据之后了…