Android - Jetpack ViewModel源码探秘 ViewModel使用场景当横竖屏切换时希望数据不丢失可以用ViewModel当成存储媒介可作为Activity Fragment通讯的媒介ViewModel的创建//Activity中构建MyViewModel ViewModelProvider(this).get(MyViewModel::class.java) //ViewModelProviders类中 public ViewModelProvider(NonNull ViewModelStoreOwner owner) { this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory() : NewInstanceFactory.getInstance());//1 } //ViewModelStore类中 public class ViewModelStore { private final HashMapString, ViewModel mMap new HashMap();//2注释1构建ViewModelProvider的参数有两个第一个参数owner.getViewModelStore()它是vm的存储器会对vm进行缓存第二个参数是构建vm的Factory默认是SavedStateViewModelFactory类型其内部通过反射构建ViewModel比较简单就不展开了注释2ViewModelStore用HashMap缓存vm说到缓存好像都喜欢用hashmap得到ViewModelProvider实例后通过get获取ViewModel//ViewModelProvider类中 public T extends ViewModel T get(NonNull ClassT modelClass) { ...... return get(DEFAULT_KEY : canonicalName, modelClass);//调用下面两个参数的重载函数传入类名作为key保证Activity同类型vm只有一个实例 } //ViewModelProvider类中 public T extends ViewModel T get(NonNull String key, NonNull ClassT modelClass) { ViewModel viewModel mViewModelStore.get(key);//1 if (modelClass.isInstance(viewModel)) { ...... return (T) viewModel; } else { ...... } ...... viewModel mFactory.create(modelClass);//2 mViewModelStore.put(key, viewModel);//3 return (T) viewModel; }注释1通过ViewModel存储器来查找如果有缓存则返回对象注释2工厂构建ViewModel工厂实现具体可以看SavedStateViewModelFactory注释3缓存vm与注释1对应横竖屏切换ViewModel如何能做到数据不丢失呢只要保证横竖屏切换前后Activity获取到的ViewModelStore(owner.getViewModelStore())是同一个实例应该就可以了吧。看下owner.getViewModelStore()巧妙的地方getViewModelStore()的实现在Activity的父类ComponentActivity中//ComponentActivity类中 public ViewModelStore getViewModelStore() { ...... ensureViewModelStore();//1 return mViewModelStore; } void ensureViewModelStore() { if (mViewModelStore null) { NonConfigurationInstances nc (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc ! null) {//1 // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore nc.viewModelStore; } if (mViewModelStore null) {//2 mViewModelStore new ViewModelStore(); } } }注释1getLastNonConfigurationInstance不为空则给mViewModelStore赋值看注释得知NonConfigurationInstances缓存了viewModelStore;注释2如果mViewModelStore为空则构建到这里我们得知getLastNonConfigurationInstance是获取缓存viewModelStore入口对应横竖屏切换后获取vm的场景那么是否就提供了保存viewModelStore的入口对应横竖屏切换前保存vm的场景//ComponentActivity类中 public final Object onRetainNonConfigurationInstance() { ...... NonConfigurationInstances nci new NonConfigurationInstances(); nci.custom custom; nci.viewModelStore viewModelStore; return nci; }代码比较简单构建NonConfigurationInstances将viewModelStore赋值给内部属性onRetainNonConfigurationInstance提供了保存viewModelStore的入口那么它是在哪里被调用呢应该不是Activity因为横竖屏切换Activity对象显然就被销毁了那么就有必要了解下横竖屏切换的流程。横竖屏切换的大体流程ActivityThread.handleRelaunchActivity是横竖屏切换的入口当时分析销毁恢复的时候有一个误区以为就是AMS先handleDestroyActivity再handleLaunchActivity没想到还有一个handleRelaunchActivity函数。//ActivityThread类中 public void handleRelaunchActivity(ActivityClientRecord tmp, PendingTransactionActions pendingActions) { ...... handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents, pendingActions, tmp.startsNotResumed, tmp.overrideConfig, handleRelaunchActivity);//主要调用handleRelaunchActivityInner ...... } //ActivityThread类中 private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges, ListResultInfo pendingResults, ListReferrerIntent pendingIntents, PendingTransactionActions pendingActions, boolean startsNotResumed, Configuration overrideConfig, String reason) { ...... handleDestroyActivity(r.token, false, configChanges, true, reason);//销毁 ...... handleLaunchActivity(r, pendingActions, customIntent);//重建 }这里的核心是销毁跟重建Activity猜测handleDestroyActivity对ViewModelStore进行保存handleLaunchActivity把保存的ViewModelStore再丢给Activity。看下ActivityThread.handleDestroyActivity销毁的过程public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { ActivityClientRecord r performDestroyActivity(token, finishing, configChanges, getNonConfigInstance, reason);//调用下面5个参数重载的函数 ...... } ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { ActivityClientRecord r mActivities.get(token);//1 ..... r.lastNonConfigurationInstances r.activity.retainNonConfigurationInstances();//2 ..... return r; }注释1根据token获取ActivityClientRecord这里很关键跟恢复Activity用是同一个对象注释2这里代码是不是有点熟悉但还不是最终的代码跟进去看看NonConfigurationInstances retainNonConfigurationInstances() { Object activity onRetainNonConfigurationInstance();//1 ...... NonConfigurationInstances nci new NonConfigurationInstances();//2 nci.activity activity; nci.children children; nci.fragments fragments; nci.loaders loaders; ...... return nci; }注释1这里可能有会点绕得仔细看下onRetainNonConfigurationInstance函数被ComponentActivity重写了这里返回的对象类型是ComponentActivity.NonConfigurationInstances它维护ViewModelStore属性;注释2这里的类型是Activity.NonConfigurationInstances它的activity属性是ComponentActivity.NonConfigurationInstances类型;所以最终被保存的Activity.NonConfigurationInstances类型对象持有关系:Activity.NonConfigurationInstances 持有 ComponentActivity.NonConfigurationInstances对象后者持有ViewModelStore对象;类图如下可以对照源码看下销毁流程小结ActivityThread会间接将ViewModelStore保存到ActivityClientRecord当中以便恢复时使用注意这里是间接因为这中间涉及多个数据对象看下ActivityThread.handleLaunchActivity恢复的过程//ActivityThread类中 public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) { ...... final Activity a performLaunchActivity(r, customIntent);//调用下面3个参数的重载函数 ...... return a; } //ActivityThread类中 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ...... 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);//1 ..... return activity; } 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) { ...... mLastNonConfigurationInstances lastNonConfigurationInstances;//2 ...... }注释1调用attch函数注意看这个参数r.lastNonConfigurationInstances注释2看到这里是不是就很熟悉了将横竖屏前保存的lastNonConfigurationInstances取出赋值注意它里面间接维护了ViewModelStore恢复流程小结ActivityThread间接将ViewModelStore通过attach函数再丢给Activity注意这里是间接因为这中间涉及多个数据对象流程图注意是间接ViewModel.onCleared何时会被调用Activity销毁非横竖屏切换//ComponentActivity类中 public ComponentActivity() { Lifecycle lifecycle getLifecycle(); ...... getLifecycle().addObserver(new LifecycleEventObserver() { Override public void onStateChanged(NonNull LifecycleOwner source, NonNull Lifecycle.Event event) { if (event Lifecycle.Event.ON_DESTROY) { if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } }); ...... }构造函数注册监听当不是因为横竖屏导致的销毁就会清空ViewModelStore也就是清空ViewModel同类型的vm被get两次那么第一个vm对象会被清理。public class ViewModelStore { private final HashMapString, ViewModel mMap new HashMap(); final void put(String key, ViewModel viewModel) { ViewModel oldViewModel mMap.put(key, viewModel); if (oldViewModel ! null) { oldViewModel.onCleared();//旧vm被清理 } }ViewModel AndroidViewModel区别后者带Application对象前者没有。 前面分析的得知构建vm的工厂是SavedStateViewModelFactory//ViewModelProvider类中 public T extends ViewModel T get(NonNull String key, NonNull ClassT modelClass) { ...... viewModel mFactory.create(modelClass);//工厂构建vm工厂是SavedStateViewModelFactory类型 return (T) viewModel; } //SavedStateViewModelFactory类中 public T extends ViewModel T create(NonNull String key, NonNull ClassT modelClass) { ...... try { T viewmodel; if (isAndroidViewModel mApplication ! null) { viewmodel constructor.newInstance(mApplication, controller.getHandle()); } else { viewmodel constructor.newInstance(controller.getHandle()); } return viewmodel; } ...... }如果是vm是AndroidViewModel类型反射构建vm的时候传入mApplication对象SavedStateViewModelFactory是在ComponentActivity中构建构建的时候会对其mApplication对象赋值ViewModel onSaveInstanceState 场景区别相同的都可以保存数据不同点通过onSaveInstanceState保存的数据最终会到达Binder缓冲区缓冲区只有1M的空间所以只能存储轻量级的数据并且涉及到跨进程通信会额外的开销序列化、反序列化ViewModel的数据是保存在内存中不会有额外开销、MVVM 中的VM思考我们通常会把MVVM的VM跟ViewModel等同其实我觉得不一定。ViewModel可以当成VM层来使用但其他满足View跟Model之间数据驱动的、做业务处理的层都可以是VM层例如把Presenter改成用LiveData做数据驱动也可以算是VM。涉及类ViewModel、AndroidViewModel均提供数据存储后者持有Application对象ViewModeStorevm仓库负责vm的缓存以及触发清理SavedStateViewModelFactory构建vm的工厂类ViewModelProvidervm提供者负责维护ViewModeStore以及ViewModelProvider.FactoryActivityClientRecord横竖屏转换时维护ViewModeStore对象总结vm之所以在横竖屏切换后保证数据不丢失是因为vm的仓库ViewModelStore会被ActivityClientRecord保存用于恢复后重新赋值所以也就保证了vm数据不会丢失Activity销毁非横竖屏切换以及同类型的vm被get两次会触发vm onClear函数。以上分析有不对的地方请指出互相学习谢谢哦文末我总结了一些Android核心知识点以及一些最新的大厂面试题、知识脑图和视频资料解析。需要的小伙伴直接点击文末小卡片可以领取哦我免费分享给你以后的路也希望我们能一起走下去。谢谢大家一直以来的支持需要的自己领取Android学习PDF架构视频面试文档源码笔记部分资料一览330页PDF Android学习核心笔记内含8大板块Android学习的系统对应视频Android进阶的系统对应学习资料Android BAT大厂面试题有解析领取地址点击下方卡片免费领取