面试素质三联?
ViewModel
优点是啥? 答:保存数据 自动管理。Fragment
能拿Activity的ViewModel么? 答:能吧?。 = = !ViewModel
怎么管理的? 答:母鸡。ViewModel
怎么创建的? 答:母鸡啊
结果
回去等消息吧,面试官顺手评价 一个深度不够,基础不牢。
逐步拆解
1 | kotlin复制代码 private val model by lazy { |
ViewModelProvider干了啥?
1 | kotlin复制代码 public constructor( |
整体看了一下,大体就三件事, 用this的 viewModelStore,搞Factory, 搞参数。
this 里面搞大事。
原来ViewModelProvider
需要传入一个ViewModelStoreOwner
接口
1 | scss复制代码 |
看下ViewModelStoreOwner
就一个方法,返回一个ViewModelStore
1 | kotlin复制代码interface ViewModelStoreOwner { |
我们常用的ComponentActivity/Fragment
都实现了ViewModelStoreOwner
和HasDefaultViewModelProviderFactory
,我们看下他们的实现getViewModelStore()
的区别。
1 | scss复制代码 @NonNull |
简略看下核心代码,getLastNonConfigurationInstance
没有,就直接new一个返回。
ComponentActivity
还是比较简单的,相当于自己管自己。
1 | less复制代码 @NonNull |
Fragment
调用FragmentManager
的getViewModelStore()
,(下面简称Fm)并把自己传入进去了。
追一下看看 进入 Fm。
1 | less复制代码 private FragmentManagerViewModel mNonConfig; |
FragmentManagerViewModel
1 | ini复制代码FragmentManagerViewModel extends ViewModel{ |
FragmentManager
调用自己的mNonConfig
,传入了Fragment
,mNonConfig
自己就是一个FragmentManagerViewModel
。也是一个ViewModel
。
到这里我们理解Frament 其实委托给了FragmentManager
。
先看下Fm
里的mNonConfig
咋来的。看初始化
FragmentManager
1 | less复制代码 |
FragmentManagerViewModel
1 | csharp复制代码@NonNull |
我们解析三个创建
+ `if` 可以理解为`fragment`嵌套 ,去找`getChildNonConfig()`。
+ `else if` 取到`host`的`viewModelStore`,自己创建了一个 `ViewModelProvider` 缓存`FragmentManagerViewModel`类进去。
+ `else` 自己 host 取不到,自己创建一个`FragmentManagerViewModel`,传入`false`,表示不自动缓存。我们看下看下host的真面目
Fragment
1 | csharp复制代码 void performAttach() { |
这host
看下哪里初始化的
FragmentController
1 | less复制代码 |
再看下 这个mHost
怎么初始化的
1 | typescript复制代码public static FragmentController createController(FragmentHostCallback<?> callbacks) { |
追一下 进入到了FragmentActivity
1 | scala复制代码 final FragmentController mFragments = FragmentController.createController(new HostCallbacks()); |
捋一下,Fragment里面的 mNonConfig.getViewModelStore(f)
,
最后会调用到 host .getViewModelStore()
。而 host 是 FragmentActivity 里的一个内部类。 最后 调用到 FragmentActivity.this.getViewModelStore()
。 而FragmentActivity
继承了ComponentActivity
。
正常情况下来说,FragmentManager
调用的是 ComponentActivity
的getViewModelStore()
,拿到了 然后 构建了一个ViewModelProvider()
返回。
实在不行了,自己 创建一个 FragmentManagerViewModel(false)
用于处理。
ViewModelStore是个啥?
1 | kotlin复制代码open class ViewModelStore { |
一眼明白,一个map
,能存ViewModel
。 能清除,无了。
ViewModelProvider. get() 又搞了啥?
1 | kotlin复制代码 @MainThread |
扫一眼,拿传入的类名 canonicalName
前面拼接 “DEFAULT_KEY”
,如果当前store
有,且是 是当前类的实例,就强转返回。 没有就factory
创建。最后调用 also
缓存进去。看下 factory
。
前面我们知道ComponentActivity
和Fragment
实现了HasDefaultViewModelProviderFactory
。
- ComponentActivity
1 | less复制代码 @NonNull |
- Fragment
1 | ini复制代码 @NonNull |
很好他们都返回了SavedStateViewModelFactory()
。看下SavedStateViewModelFactory
的creat()
;
- SavedStateViewModelFactory.create()
1 | less复制代码 @NonNull |
一个参数调用俩参数,判断了是否是判断是否是AndroidViewModel
,再往下一看newInstance()
,反射, 绝对的反射。
到此为止,我们知道 get()
其实就是缓存则取,没缓存就反射搞个对象 ,also 缓存进去。
至此,还没成艺术。
ViewModel 的自我修养(管理)?
认真阅读的朋友们都知道,Fragment
正常情况下用FragmentActivity
的ViewModelStore
。
- 看下
FragmentActivity
的 父类ComponentActivity
构造参数。
1 | less复制代码public ComponentActivity() { |
我们看到 做了个监听,在 *ON_DESTROY*
的时候, 判断了下!isChangingConfigurations()
也就是 不是发生了配置变化,是真正销毁的时候,调用了 getViewModelStore().clear()
;
其实就是相当于把当前缓存的ViewModel
的对象嘎嘎的清空了。
经常说ViewModel
为什么不建议持有Context
。 因为是在onDestroy
后才执行。
也为什么说旋转屏幕ViewModel
不会丢数据。因为虽然走了onDestroy
但是内部判断了是否旋转屏幕。
- 那
Fragment
怎么管理的呢
我们记得Fragment
在 this 那一步的时候,有2种 情况,一种 拿FragmentActivity
的ViewModelStore 。一种自己构建了一个FragmentManagerViewModel()
;
这个mNonConfig
其实在创建后 赛进了mFragmentStore
。
1 | ini复制代码 if (parent != null) { |
mFragmentStore.setNonConfig(mNonConfig);
把mNonConfig
传出去了,追着看一下。
进入 FragmentStore
。
发现 FragmentStore
是在FragmentManager
初始化就创建了
1 | java复制代码 private final FragmentStore mFragmentStore = new FragmentStore(); |
FragmentStore
里调用mNonConfig
查看下被调用的地方。
1 | csharp复制代码 FragmentManagerViewModel getNonConfig() { |
进入FragmentManager.java
->clearBackStackStateViewModels()
这里去判断了哪些Fragment 需要清理
1 | ini复制代码private void clearBackStackStateViewModels() { |
->dispatchDestroy()
1 | scss复制代码 void dispatchDestroy() { |
-> FragmentController.java
->dispatchDestroy()
1 | csharp复制代码public void dispatchDestroyView() { |
-> FragmentActivity.java
->onDestroy(){}
1 | scss复制代码 @Override |
把整个调用反过来看一下,FragmentActivity
在onDestroy
调用
mFragments.dispatchDestroy()
; (FragmentController.java
)
-> FragmentManager.dispatchDestroy()
(FragmentManger.java
)
-> clearBackStackStateViewModels()
。
可以理解为FragmentActivity
销毁才销毁。
另外 FragmentStateManager
里 重新构建的时候,也会销毁清理对应的ViewModel
。感兴趣可以看一下。
1 | scss复制代码void moveToExpectedState{ |
面试的侃侃而谈
- 优势
+ 保存数据,页面变化能缓存
+ 自动管理,页面销毁自动清理
+ `Fragment`和 `Activity`可共用。
Fragment
能拿Activity
的ViewModel
么?
+ 能 ,毕竟`FragmentManger` 那向上管理,其实取的就是上层`FragmentActivity`的`ViewModelStore`。
- 怎么自动管理的?
+ `ComponentActivity` 监听`onDestroy()` ,清理
+ `Fragment`在`FragmentActivity` 的 `onDestroy()` 会清理。
ViewModelStore
知道么?
+ 知道 ,一个`map` 就是干。
- 知道怎么创建的么?
+ 内部 `factory` 反射就是干。
- 为啥旋转还能保存数据?
判断了是配置变化,如旋转屏幕等,等到真正销毁才清空。
- 剩下的自行发挥
本文转载自: 掘金