首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Android ViewModel 原理浅析

  • 25-04-23 15:08
  • 3573
  • 11524
juejin.cn

开始的时候我们先想三个问题



1.ViewModel是怎么创建的

2.ViewModel的存储方式是什么,他是怎么和LifecycleOnwer绑定的

3.如果ViewModel的生命周期和LifecycleOnwer绑定,为什么ViewModel在屏幕旋转这种Activity销毁重建的情况下,能继续持有数据



1.ViewModel是怎么创建的

一般情况下,我们获取某个viewModel的时候,是调用以下方法获取

scss
代码解读
复制代码
val viewModel = ViewModelProvider(lifecycleOwner, viewModelFactory).get(viewModelClass)



这里的话是创建一个ViewModelProvider对象,构造函数传入lifecycleOwner, viewModelFactory,然后通过viewModelClass这个key去获取对应的viewModel。(其实这里隐约可以猜出,底层应该是类似于map的方式,viewModelClass为key,viewModel对象为value)



点进ViewModelProvider的这个构造方法,可以看到它实际调用的是另一个构造方法 this(owner.getViewModelStore(), factory),注意这里ViewModelStore是通过owner去获取的,那这里的ViewModelStore和Factory就是ViewModelProvider的关键成员了。

less
代码解读
复制代码
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) { this(owner.getViewModelStore(), factory); }  public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; mViewModelStore = store; }



接下来我们再看看get方法,可以看到最终还是调了另外一个get的重载方法get(@NonNull String key, @NonNull Class modelClass),传入了DEFAULT_KEY + ":" + canonicalName(class的一个方法,获取类的规范化名称)作为key。

然后真正的get方法里,通过这个key,从mViewModelStore获取viewmodel,如果为null,则通过Factory新建一个。

less
代码解读
复制代码
@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); }  @SuppressWarnings("unchecked") @NonNull @MainThread public <T extends ViewModel> T get(@NonNull String key, @NonNull Class modelClass) { // 注释1 通过key获取viewmodel 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; }



ViewModelStore

从刚才的代码中,我们可以看到ViewModelStore是一个key value形式保存的数据结构,那接下来我们看看ViewModelStore的源码是怎么实现的。

typescript
代码解读
复制代码
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,在这基础知识,新增了clear的方法。



Factory
less
代码解读
复制代码
public interface Factory { @NonNull T create(@NonNull Class modelClass); }

可以看到Factory其实就是一个接口,具体的create方法需要自己实现,这里我们看一下NewInstanceFactory的默认实现。



typescript
代码解读
复制代码
public static class NewInstanceFactory implements Factory {  private static NewInstanceFactory sInstance;  /** * Retrieve a singleton instance of NewInstanceFactory. * * @return A valid {@link NewInstanceFactory} */ @NonNull static NewInstanceFactory getInstance() { if (sInstance == null) { sInstance = new NewInstanceFactory(); } return sInstance; }  @SuppressWarnings("ClassNewInstance") @NonNull @Override public extends ViewModel> T create(@NonNull Class modelClass) { //noinspection TryWithIdenticalCatches try { return modelClass.newInstance(); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } } }

可以看到是通过反射的方式,new出了一个实例。



那这样的话,前面提到的两个问题的答案就很明显了



1.ViewModel是怎么创建的

ViewModel是通过ViewModelProvider获取的,它的创建则是通过传入的Factory.create进行创建,默认的实现NewInstanceFactory是直接通过反射的方式new出了viewmodel的实例。

2.ViewModel的存储方式是什么,他是怎么和LifecycleOnwer绑定的

ViewModel是存储在ViewModelStore里的,ViewModelStore底层是一个HashMap,value的值就是ViewModel,而key值则是DEFAULT_KEY + ":" + canonicalName。

他是怎么和LifecycleOnwer绑定的呢,在ComponentActivity的构造函数里,添加了一个lifecycle的观察者,在lifecycle生命周期变为ON_DESTROY的时候,且不是isChangingConfigurations的时候,清除这个ViewModelStore。

less
代码解读
复制代码
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(); } } } });



从上面的结论中我们可以得出第三个问题的答案,在满足不是isChangingConfigurations的时候,ViewModel就算在Activity销毁后,也不会clear。这个isChangingConfigurations是Activity的一个成员变量,其含义是用于判断当前 Activity 是否因为配置变更而被系统销毁并重建。

那这样的话还有个额外的问题就是,Activity重建了肯定会重新创建一个实例,那此时ViewModel存在哪里呢。

less
代码解读
复制代码
@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."); } if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; }



通过getViewModelStore可以知道,ViewModelStore是通过一个叫NonConfigurationInstances的东西来获取的,那我们继续看看NonConfigurationInstances是哪来的。



arduino
代码解读
复制代码
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) { attachBaseContext(context); // 省略 mLastNonConfigurationInstances = lastNonConfigurationInstances; // 省略 }



通过mLastNonConfigurationInstances赋值的地方我们可以看到,他是在Activity.attach里被复制的,那这个Activity.attach是什么时候被调用的呢。他其实是在ActivityThread里的performLaunchActivity被调用的,如果看过startActivity的源码应该对这个方法不陌生。

typescript
代码解读
复制代码
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; // 省略 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); // 省略  return activity; }



可以看到这个lastNonConfigurationInstances是被ActivityClientRecord所持有的,那这个ActivityClientRecord是存在ActivityThread里用于跟踪和管理Activity运行时状态的地方,每个运行的 Activity 都对应一个 ActivityClientRecord实例

ini
代码解读
复制代码
final ArrayMap mActivities = new ArrayMap<>();



这下我们就明白了ActivityThread通过存储ActivityClientRecord.mLastNonConfigurationInstances 从而帮Activity存储了他所需要的ViewModelStore,这样不管这个Activity怎么销毁重建,只要ActivityThread还在记录他的状态,那ViewModelStore就不会被销毁,这样在重建后也可以马上拿到ViewModelStore还原数据。

注:本文转载自juejin.cn的雨和卡布奇诺的文章"https://juejin.cn/post/7495776682139205672"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

140
Android
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top