欢迎光临散文网 会员登陆 & 注册

Android ViewModel源码分析

2022-03-15 17:10 作者:房顶上的铝皮水塔  | 我要投稿

本文将分成以下几个部分:

  1. ViewModel如何被构建出来?

  2. ViewModel为什么能存储数据?

PART 1 ViewModel的构建流程

我们通常可以使用by viewModels()的方式构建ViewModel的实例,viewModels()作为ComponentActivity的扩展函数,会返回一个ViewModelLazy。

ViewModelStore和ViewModelFactory

这里需要注意一下,传入的viewModelStore和defaultViewModelProviderFactory都是ComponentActivity的属性。

ViewModelStore基于HashMap的结构进行ViewModel的存储:

当ComponentActivity被销毁时清除其中的数据:

Factory默认使用的是SavedStateViewModelFactory,具体的构建过程下面会说到。


说回刚才的ViewModelLazy,Lazy作为一个懒加载的通用接口,其中的value属性表示在Lazy对象的生命周期过程中只能被修改一次的属性。所以具体看看ViewModelLazy对于value的get方法的重写:

首先是调用ViewModelProvider的构造函数,然后调用get方法:

在后面的get方法中可以看到,如果ViewModelStore中的map存在缓存,我们直接从缓存中找到ViewModel,否则调用KeyedFactory#create,SavedStateViewModelFactory就是一个KeyedFactory的子类。

create方法中通过构建SavedStateHandleController,然后向ViewModel中塞入了一个SavedStateHandle,从而构建了ViewModel。SavedStateHandle就是ViewModel能够进行状态保存的关键所在,这里先卖个关子,最后一节会详细解释。

至此,我们已经稍微涉及到了ViewModel状态保存的关键角色,这些关键角色在后面的调用链中我使用不同颜色的方块表示:

以上的ViewModel的构建部分已经介绍完毕,总结如下图:

ViewModel创建过程总结:

  1. ViewModel创建过程在Kotlin中基于by 语法,但是本质上还是通过ViewModelProvider创建,所以我们在Java中调用的是ViewModelProvider的方法。

  2. ViewModelProvider创建ViewModel的奥秘在于ComponentActivity中的两个关键角色,ViewModelStore和SavedStateViewModelFactory。如果存在ViewModel的缓存,ViewModelStore会取出,如果没有缓存,通过Factory构建。

  3. Factory构建时放入了SavedStateHandle,它是ViewModel可以进行状态保存的奥秘所在。

下面进入PART2,详细讲述SavedState这些兄弟是如何工作的。

PART2:ViewModel如何保存状态

我们先由上至下阅读,先从顶部的结构一直到底部,最后再对涉及到的角色进行一个总结

SavedStateRegistryController

在ComponentActivity中会调用Controller的performRestore和performSave方法:

具体看看实现:

这些Controller可以说是SavedStateRegistry的外观,它持有LifecycleOwner和Registry的实例:

并且具体的操作也依托着Registry的

同名方法。下面我们看看Registry中的perform方法的实现

SavedStateRegistry

看看其中的两个perform方法:

performRestore的逻辑还算简单,就是将Key所对应的内容读取到mRestoredState这个Bundle中。performSave的逻辑总结而言就是将Map中的SavedStateProvider和mRestoreState都存在了mBundle中。我们在SavedStateHandle中还会见到SavedStateProvider发挥它的作用,所以说到的时候再具体说。

所以这块的逻辑是这样,当onCreate调用时,会执行performRestore,将Bundle中的数据寸在SavedStateRegistry的Bundle mRestoreState中;当执行onSaveInstanceState时,会从Bundle mRestoreState中读取数据,具体怎么读的,怎么使用的Bundle中的数据的,还记得SavedStateHandle吗,就是存放在ViewModel中的那个,现在我们来看看:

SavedStateHandle

我们先回顾一下构建ViewModel的逻辑时的图:

在Factory的create方法的调用链上还调用了SavedStateHandleController#attachToLifecycle方法:

这个方法除了做了生命周期的操作之外,还将Provider注册到了SaveStateRegistry中。我们现在来看SavedStateHandle中的Provider的实现:

所以返回的Bundle中是一个ArrayList<String>和ArrayList<Object>的结构。

这里其实我没分析特别明白,我不知道这部分的Provider绑定到ViewModel之后如何取出数据的,而且官方文档中也没说清楚。

我们看看官方给出的使用ViewModel保存状态的例子中是如何使用到这些结构的。

直接使用SavedStateHandle保存数据

数据在进程终止时会保存下来。SavedStateHandle会将结果存在mRegular中,如果Activity被销毁,就回调用onSaveInstanceState->SavedStateRegistry#performSave->Provider#saveState获取到对应的数据。在SavedStateProvider#saveState是将键名和值,使用put方法放入Bundle。

通过写SavedStateProvider

在官方的例子中,如果我们想要使用保存状态的功能,分成以下几步:

  1. 定义Provider

  2. 将Provider注册到SavedStateRegistry

其实这个写法和上面的相似,但是因为在创建ViewModel的时候,尤其是使用by viewModels时,会自动创建SavedStateHandle。我们在自定义的Provider中实现saveState方法,在onSaveInstanceState时,也会通过SavedStateRegistry#performSave调用。进而也会保存在Bundle中

总结

虽然写了这么多,但是其实我个人其实对于ViewModel的状态保存还不是特别理解。根据我个人的理解,SavedState相关的几个类的关系如下:

  1. ViewModel的创建通过ViewModelProvider实现,主要通过ViewModelStore和Factory这样两个重要的角色。首先会从缓存中找是否存在ViewModel,如果没有在调用SavedStateViewModelFactory#create构建新的ViewModel。而且ViewModel的生命周期很长,通过Lifecycle回调进行监听,如果是onDestroy,就回调用ViewModel#onClear。

  2. ViewModel实现状态的保存有赖于SavedStateHandle及其内部的SavedStateProvider实现。当Activity要被销毁时,调用SavedStateRegistry#performSave就会调用系统默认注册的SavedStateHandle中的Provider,保存数据。

  3. 用户可以通过传入SavedStateHandle(在Factory#create中反射调用了一个具有SaveStateHandle的构造方法,这个方法在ViewModel中是没有的)实现保存状态;也可以实现Provider,两者的原理大体相同。

鉴于我对于状态保存还理解不深入,之后遇到开发问题再过来补充!


Android ViewModel源码分析的评论 (共 条)

分享到微博请遵守国家法律