Android JetPack 深入分析ViewModel源码

文章目录

    • 前言
    • 源码分析
      • ViewModel是如何创建的?
        • ViewModelProvider(this)做了什么?
          • 小结
        • get(MyViewModel::class.java)做了什么?
          • 小结
      • ViewModel是如何实现配置更改后数据恢复的?
          • 整体时序图
    • 结语

前言

本文主要分析ViewModel相关源码,相关使用不再赘述,可参考Android ViewModel使用;

ViewModel 概览

Google官方给的ViewModel定义如下:

ViewModel类旨在`以注重生命周期的方式存储和管理界面相关数据`。ViewModel类`让数据可在发生屏幕旋转等配置更改后继续留存`

定义主要提到两个关键点:

  • 生命周期
    ViewModel生命周期
    上图是Google官网提供的ViewModel生命周期图示,可以看到ViewModel的生命周期是从onCreate创建到完成并销毁Finished,开发中经常结合LiveData一起使用;

  • 配置更改后数据留存
    我们知道当屏幕旋转的时候,Activity会销毁重建,如果我们想恢复之前的数据,之前一般是通过onSaveInstanceState()来实现,内部是通过Bundle来实现,此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,而ViewModel可以很简单的实现这个功能,并存储较大的数据;

源码分析

ViewModel是如何创建的?

 	val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

以上是ViewModel创建代码

ViewModelProvider(this)做了什么?

### ViewModelProvider.javapublic ViewModelProvider(@NonNull ViewModelStoreOwner owner) {this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory(): NewInstanceFactory.getInstance());}

ComponentActivity则实现了ViewModelStoreOwnerHasDefaultViewModelProviderFactory接口;
因此我们只需要关注ComponentActivity中对应的方法;

  1. getViewModelStore()
  2. getDefaultViewModelProviderFactory()

我们先分析getViewModelStore:

	### ComponentActivity.getViewModelStorepublic ViewModelStore getViewModelStore() {...ensureViewModelStore();return mViewModelStore;}

可以看到getViewModelStore()调用了ensureViewModelStore(),其对应代码如下:

### ComponentActivity.ensureViewModelStorevoid ensureViewModelStore() {if (mViewModelStore == null) {NonConfigurationInstances nc =(NonConfigurationInstances) getLastNonConfigurationInstance();if (nc != null) {// Restore the ViewModelStore from NonConfigurationInstancesmViewModelStore = nc.viewModelStore;}if (mViewModelStore == null) {mViewModelStore = new ViewModelStore();}}}static final class NonConfigurationInstances {Object custom;ViewModelStore viewModelStore;}

ensureViewModelStore方法中,会判断mViewModelStore是否为null,如果为null的话,会先尝试调用getLastNonConfigurationInstance()方法获取,如果获取不到,则直接new创建一个ViewModelStore

我们看下ViewModelStore类:

public class ViewModelStore {private final HashMap<String, ViewModel> mMap = new HashMap<>();final void put(String key, ViewModel viewModel) {ViewModel oldViewModel = mMap.put(key, viewModel);if (oldViewModel != null) {oldViewModel.onCleared();}}final ViewModel get(String key) {return mMap.get(key);}Set<String> keys() {return new HashSet<>(mMap.keySet());}/***  Clears internal storage and notifies ViewModels that they are no longer used.*/public final void clear() {for (ViewModel vm : mMap.values()) {vm.clear();}mMap.clear();}
}

其内部创建了一个HashMap,以ViewModel的CanonicalName为key,ViewModel对象为value,存储Activity中创建的ViewModel对象;

我们再看下getDefaultViewModelProviderFactory()方法:

### ComponentActivity.getDefaultViewModelProviderFactory()public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {...if (mDefaultFactory == null) {mDefaultFactory = new SavedStateViewModelFactory(getApplication(),this,getIntent() != null ? getIntent().getExtras() : null);}return mDefaultFactory;}

主要是创建一个SavedStateViewModelFactory对象返回,我们看下SavedStateViewModelFactory的构造方法

    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();}

我们看下mFactory的创建,application 肯定不为null,因此会调用ViewModelProvider.AndroidViewModelFactory.getInstance(application)返回一个AndroidViewModelFactory对象

    public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {private static AndroidViewModelFactory sInstance;public static AndroidViewModelFactory getInstance(@NonNull Application application) {if (sInstance == null) {sInstance = new AndroidViewModelFactory(application);}return sInstance;}private Application mApplication;public AndroidViewModelFactory(@NonNull Application application) {mApplication = application;}@NonNull@Overridepublic <T extends ViewModel> T create(@NonNull Class<T> modelClass) {if (AndroidViewModel.class.isAssignableFrom(modelClass)) {try {return modelClass.getConstructor(Application.class).newInstance(mApplication);} catch (NoSuchMethodException e) {throw new RuntimeException("Cannot create an instance of " + modelClass, e);} catch (IllegalAccessException e) {throw new RuntimeException("Cannot create an instance of " + modelClass, e);} catch (InstantiationException e) {throw new RuntimeException("Cannot create an instance of " + modelClass, e);} catch (InvocationTargetException e) {throw new RuntimeException("Cannot create an instance of " + modelClass, e);}}return super.create(modelClass);}}

可以看到AndroidViewModelFactory主要是用来生产ViewModel的,其create()方法:根据传入的ViewModel类型【继承自AndroidViewModel或ViewModel】,通过反射创建ViewModel对象;

小结

调用ViewModelProvider(this)方法主要做了以下两件事:

  1. 获取ViewModelStore对象,如果ViewModelStore为null,会先尝试调用getLastNonConfigurationInstance()方法获取,获取不到则直接通过new创建,内部通过HashMap实现ViewModel对象的存储;

  2. 创建AndroidViewModelFactory对象,其内部提供了create(),使用反射创建对应的ViewModel对象;

get(MyViewModel::class.java)做了什么?

	### ViewModelProvider.javapublic <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);}@NonNull@MainThreadpublic <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {ViewModel viewModel = mViewModelStore.get(key);if (modelClass.isInstance(viewModel)) {if (mFactory instanceof OnRequeryFactory) {((OnRequeryFactory) mFactory).onRequery(viewModel);}return (T) viewModel;} else {if (viewModel != null) {}}if (mFactory instanceof KeyedFactory) {viewModel = ((KeyedFactory) mFactory).create(key, modelClass);} else {viewModel = mFactory.create(modelClass);}mViewModelStore.put(key, viewModel);return (T) viewModel;}
小结

get(MyViewModel::class.java)方法会根据传入的ViewModel的CanonicalName,先从mViewModelStore缓存中查找,如果找到,则直接返回,如果没找到,则调用前面创建的mFactory.create()方法通过反射创建对应的ViewModel,并添加到 mViewModelStore缓存中;

以上便是ViewModel相关的创建逻辑源码分析!

ViewModel是如何实现配置更改后数据恢复的?

我们知道当屏幕旋转配置发生变化时,会调用onDestroy(),在ComponentActivity构造方法中:

   getLifecycle().addObserver(new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {if (event == Lifecycle.Event.ON_DESTROY) {// Clear out the available contextmContextAwareHelper.clearAvailableContext();// And clear the ViewModelStoreif (!isChangingConfigurations()) {getViewModelStore().clear();}}}});

当监听到Activity调用ON_DESTROY时,会调用getViewModelStore().clear(),清除ViewModelStore内部缓存的ViewModel对象,那为何重新创建后,ViewModel中持有的数据又没有丢失呢?

Activity销毁时会调用ActivityThread.handleDestroyActivity()方法:

  public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,boolean getNonConfigInstance, String reason) {ActivityClientRecord r = performDestroyActivity(token, finishing,configChanges, getNonConfigInstance, reason);if (r != null) {...}mSomeActivitiesChanged = true;}

handleDestroyActivity方法中又会调用performDestroyActivity()方法:

  ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,int configChanges, boolean getNonConfigInstance, String reason) {...ActivityClientRecord r = mActivities.get(token);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;}

performDestroyActivity方法中会调用activity.retainNonConfigurationInstances()

    NonConfigurationInstances retainNonConfigurationInstances() {Object activity = onRetainNonConfigurationInstance();...return nci;}

retainNonConfigurationInstances()方法中又调用onRetainNonConfigurationInstance(),其在ComponentActivity中具体实现代码如下:

    public final Object onRetainNonConfigurationInstance() {Object custom = onRetainCustomNonConfigurationInstance();ViewModelStore viewModelStore = mViewModelStore;if (viewModelStore == null) {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;}

可以看到会将viewModelStore保存到NonConfigurationInstances对象里返回,结合performDestroyActivity源码,最终会保存到ActivityClientRecord.lastNonConfigurationInstances属性中;

Activity重新创建的时候,会执行ActivityThread.performLaunchActivity()方法:

    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,r.assistToken);...return activity;}

会调用 activity.attach()方法将lastNonConfigurationInstances对象传入,并赋值给Activity中的mLastNonConfigurationInstances属性;同时在ComponentActivity的构造方法中找到如下代码:

        getLifecycle().addObserver(new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {ensureViewModelStore();getLifecycle().removeObserver(this);}});

ensureViewModelStore()方法上面我们已经看到,内部会先调用getLastNonConfigurationInstance()方法获取mViewModelStore对象,getLastNonConfigurationInstance()方法返回的正是mLastNonConfigurationInstances属性;从而实现ViewModel数据的恢复!

整体时序图

ViewModelStore时序图

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/2031.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JUC之LockSupport和中断

文章目录 1 线程中断机制1.1 什么是线程中断机制1.2 三大中断方法1.3 如何中断运行中的线程1.3.1 通过volatile变量实现1.3.1 通过AtomicBoolean实现1.3.1 通过interrupt和isInterrupted api实现 2 LockSupport2.1 为什么会出现LockSupport2.2 两道面试题 参考材料 1 线程中断机…

移动隔断屏风墙,无地轨设计,空间灵活应用

移动隔断屏风墙是一种非常适合办公室的设计选择&#xff0c;它可以提供灵活的办公空间布局&#xff0c;并且无地轨设计可以避免地面安装轨道&#xff0c;给空间带来更大的自由度。以下是一些关于移动隔断屏风墙的特点和设计建议&#xff1a; 1. 灵活应用&#xff1a;移动隔断屏…

JavaScript进阶----《getter 和 setter 是什么》

前言&#xff1a; 这两个属性在学习前端的时候看到过&#xff0c;但是由于项目中没有用到过&#xff0c;所以一直没有细致的了解。今天 review 同事代码的时候&#xff0c;遇到了这个写法&#xff0c;看了半天也不知道如何处理。再不学习真的以后连别人的代码都不知道什么意思了…

百度智驾,与车路协同说「再见」

作者 | 魏启扬 来源 | 洞见新研社 在经历了裁员&#xff0c;全员停发年终奖之后&#xff0c;百度对智能交通事业部&#xff08;ACE&#xff09;的治理还在继续。 不久前&#xff0c;有媒体爆料称&#xff0c;百度已经将智能交通事业部&#xff08;ACE&#xff09;从原来的智能…

微信小程序——分页组件的创建与使用

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

C++类与对象(默认成员函数之拷贝构造函数)

接前几次的类与对象的默认函数的知识点&#xff0c;下来面是默认成员函数中的拷贝构造函数。是的&#xff0c;它的名字是拷贝构造函数&#xff0c;他其实也是一种构造函数&#xff0c;为什么呢&#xff1f;接下来你就知道了&#xff0c;我们直接看看代码&#xff0c;如下&#…

kafka入门,发送原理和生产者重要参数(三)

发送原理 在消息发送过程中&#xff0c;涉及两个线程&#xff0c;main线程和Sender线程。在main线程中创建了一个双端队列&#xff0c;RecordAccumulator,Sender过程不断从RecordAccumulator中拉取消息发送到Kafka Broker batch size:只有数据累计到batch.size之后&#xff0…

C#winform listBox组件批量删除

修改listBox组件属性&#xff1a;可以选中多个板坯号 选中板坯列表&#xff0c;在界面上点击删除按钮&#xff0c;触发删除方法deleteList&#xff1a; private void deleteList() { ListBox.SelectedIndexCollection sic listBoxProducts.SelectedIndice…

虹科分享|如何防范MOVEit transfer漏洞|高级威胁防御

美国网络安全和基础设施安全局(CISA)承认&#xff0c;它正在向几个联邦机构提供支持&#xff0c;这些机构在Progress(前身为IpSwitch)MOVEit传输解决方案中暴露出漏洞后被攻破。根据CISA发布的一份警报和网络安全公告&#xff0c;CL0P勒索软件团伙一直在积极利用漏洞进行数据外…

react中基于腾讯地图的地图选点,地址搜索逆向定位获取经纬度

react中基于腾讯地图的地图选点&#xff0c;地址搜索逆向定位获取经纬度 效果示例图地图组件tencentMap/index.jsx样式map.scss 使用案例 效果示例图 地图组件tencentMap/index.jsx import { useEffect, useRef, useState } from "react"; import "./map.scss&…

数据结构--单链表的插入删除

数据结构–单链表的插入&删除 目标 单链表的插入&#xff08;位插、前插、后插&#xff09; 单链表的删除 单链表的插入 按为序插入(带头结点) ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。 思路&#xff1a;找到第i-1个结点,将新结点插入其…

SpringMVC

SpringMVC常用注解: 1&#xff1a;Controller:用于标记控制器类&#xff0c;表示该类是可以处理HTTP请求的。 2&#xff1a;RequestMapping:用于映射URL和处理方法。可以在类和方法上&#xff0c;类级别的RequestMapping会对其中所有的方法进行URL映射。参数支持Ant-style路径…