Android框架组件ViewModel

LiveData与ViewModel都是Android官方架构组件(Android Architecture Components)之一。

1.前言

虽说这篇是说LiveData与ViewModel,但是或多或少都有涉及另外一个组件:Lifecycles 。它们连同Room都是在17年谷歌IO大会推出的,当时还是预览版,大致17年底时推出了正式版。到今年的IO大会过后,又增加了许多新成员。

可以看到27.0.0的v7库有依赖Lifecycles。

当时Lifecycles有集成进SupportActivity。

其实一开始我没有太当回事。。。直到27.1.0以后:

好吧,今天的主角出现了,LiveData与ViewModel。看到这里我觉得是该了解一波了。

顺便看一下截止目前最新的v7:

发现好多常用的组件分离出了v4包,比如ViewPager、SwipeRefreshLayout,这里就不多说了。

2.优势

LiveData 是一个可以感知 Activity 、Fragment生命周期的数据容器。当 LiveData 所持有的数据改变时,它会通知相应的界面代码进行更新。同时,LiveData 持有界面代码 Lifecycle 的引用,这意味着它会在界面代码(LifecycleOwner)的生命周期处于 started 或 resumed 时作出相应更新,而在 LifecycleOwner 被销毁时停止更新。

上面的描述介绍了LiveData的优点:不用手动控制生命周期,不用担心内存泄露,数据变化时会收到通知。

ViewModel 将视图的数据和逻辑从具有生命周期特性的实体(如 Activity 和 Fragment)中剥离开来。直到关联的 Activity 或 Fragment 完全销毁时,ViewModel 才会随之消失,也就是说,即使在旋转屏幕导致 Fragment 被重新创建等事件中,视图数据依旧会被保留。ViewModels 不仅消除了常见的生命周期问题,而且可以帮助构建更为模块化、更方便测试的用户界面。

ViewModel的优点也很明显,为Activity 、Fragment存储数据,直到完全销毁。尤其是屏幕旋转的场景,常用的方法都是通过onSaveInstanceState()保存数据,再在onCreate()中恢复,真的是很麻烦。

其次因为ViewModel存储了数据,所以ViewModel可以在当前Activity的Fragment中实现数据共享。

那么LiveData与ViewModel的组合使用可以说是双剑合璧,而Lifecycles贯穿其中。

ViewModel生命周期

 我们看看ViewModel的生命周期,在官方文档中,用下面这张图来描述ViewModel的生命周期。

5777390-ccc0b4e602a37bae.png

上图是用Activity作为例子,左侧表示Activity的生命周期状态,右侧绿色部分表示ViewModel的生命周期范围。当屏幕旋转的时候,Activity会被recreate,Activity会经过几个生命周期方法,但是这个时候ViewModel还是之前的对象,并没有被重新创建,只有当Activity的finish()方法被调用时,ViewModel.onCleared()方法会被调用,对象才会被销毁。这张图很好的描述了是当Activity被recreate时,ViewModel的生命周期.

另外,有个注意的地方:在ViewModel中不要持有Activity的引用。为什么要注意这一点呢?从上面的图我们看到,当Activity被recreate时,ViewModel对象并没有被销毁,如果Model持有Activity的引用时就可能会导致内存泄漏。那如果你要使用到Context对象怎么办呢,那就使用ViewModel的子类AndroidViewModel吧.

用于组件间通信

下面我们看下ViewModel用于Fragment之间通信的例子。

5777390-067af965a59d0fb3.png
5777390-066c1e050041fefd.png
5777390-feb1d7fa8559aff7.png
5777390-a254024e5e5f612f.png
5777390-6049ba115c961f9a.png

代码逻辑很简单,首先定义一个CommunicateViewModel类,继承自ViewModel。然后定义两个Fragment:FragmentOne和FragmentTwo。在FragmentOne中改变CommunicateViewModel中LiveData保存的数据,然后在FragmentTwo中会收到数据改变的通知。这样一个过程就完成了FragmentOne和FragmentTwo之间的一次简单的通信。至于其中的原理,相信看过LiveData这篇文章话,这都不是问题了。在上面的例子中有个小陷阱:在初始化mCommunicateViewModel时,ViewModelProviders.of()方法传入的是Activity对象,如果你改成Fragment对象,那FragmentTwo里就只能傻等了,永远不会收到数据改变的通知。因为如果传给方法ViewModelProviders.of()的对象不同时,最终得到的就不是同一个ViewModel对象,这一点也可以通过打印两个Fragment中的mCommunicateViewModel验证。

实现原理

  在讲ViewModel的原理时,我们还是延续前面的传统,先理清ViewModel相关的类图,然后理清ViewModel使用的时序图,最后就是根据时序图分析源码。

类图

5777390-3960415ab6c24405.png

ViewModelProviders是ViewModel工具类,该类提供了通过Fragment和Activity得到ViewModel的方法,而具体实现又是有ViewModelProvider实现的。ViewModelProvider是实现ViewModel创建、获取的工具类。在ViewModelProvider中定义了一个创建ViewModel的接口类——Factory。ViewModelProvider中有个ViewModelStore对象,用于存储ViewModel对象。

ViewModelStore是存储ViewModel的类,具体实现是通过HashMap来保存ViewModle对象。

ViewModel是个抽象类,里面只定义了一个onCleared()方法,该方法在ViewModel不在被使用时调用。ViewModel有一个子类AndroidViewModel,这个类是便于要在ViewModel中使用Context对象,因为我们前面提到是不能在ViewModel中持有Activity的引用。

ViewModelStores是ViewModelStore的工厂方法类,它会关联HolderFragment,HolderFragment有个嵌套类——HolderFragmentManager。


源码分析

 根据上面时序图中的内容,我们从两个方面着手研究下ViewModel相关源码。

获取ViewModel对象

 得到ViweModel对象,首先是通过ViewModelProviders.of()方法得到ViewModelProvider对象,然后调用ViewModelProvider.get()方法得到ViewModel对象。

一、得到ViewModelProvider对象

 直接看ViewModelProviders类的具体实现

5777390-753ca8718dc322d8.png
5777390-7b067e000a199791.png
5777390-5091a0f381af399b.png

ViewModelProviders提供了四个of()方法,四个方法功能类似,其中of(FragmentActivity activity, Factory factory)和of(Fragment fragment, Factory factory)提供了自定义创建ViewModel的方法。我们在时序图中使用of(Fragment fragment)最为研究对象,这里我们也从该方法开始研究,在该方法中主要有两个步骤。

1,判断是否需要初始化默认的Factory

 这一过程很简单:通过调用initializeFactoryIfNeeded()方法判断是否需要初始化mDefaultFactory变量。在此之前又会判断Fragment的是否Attached to Activity,Activity的Application对象是否为空。

2,创建一个ViewModelProvider对象。

 创建ViewModel对象看似很简单,一行代码搞定new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)。但是里面的里面的逻辑比较深,我们慢慢把它抽丝剥茧,看看有多深。

 先看看ViewModelStores.of()方法

5777390-f1406be3687a50aa.png

又是一行代码,holderFragmentFor()是HolderFragment的静态方法,HolderFragment继承自Fragment。我们先看holderFragment()方法的具体实现

5777390-59f3f2e5d69c72d6.png

额,holderFragmentFor()又是一行代码,继续看HolderFragmentManager.holderFragmentFor()方法的具体实现

5777390-941d8e73c3154e61.png
5777390-fe1578da4c663f41.png

在createHolderFragment()方法中,你也许发现调用FragmentTransaction.add()方法并没有传一个定义在layout文件中的id进去,而且你去看HolderFragment并没有重写Fragment.onCreateView()方法。这个有点反常,这是为什么呢? 这个疑惑可以从HolderFragment()构造方法中得到解答。在构造方法中调用了setRetainInstance(true)方法,该方法会使得HolderFragment所在的Activity被recreate时,HolderFragment不会被recreate,自然它就不会走onDestroy()、onCreate()方法。为什么不让HolderFragment被recreate呢?这里先卖个关子,等到在讲时序图中步骤二时我们再讲。

 到此为止,我们已经得到了ViewStore对象,前面我们在创建ViewModelProvider对象是通过这行代码实现的new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)现在再看下ViewModelProvider的构造方法

二、得到ViewModel对象

 在上面我们已经得到了ViewModelProvider对象,现在就可以通过ViewModelProvider.get()方法得到ViewModel对象,继续看下该方法的具体实现

5777390-445483f3b94f69e1.png

  ViewModelProvider.get()方法比较简单,该说的都在注释中写明了。最后我们看下ViewModelStore类的具体实现

5777390-8507918124935e12.png

ViewModelStore是缓存ViewModel的类,put()、get()方法用于存取ViewModel对象,另外提供了clear()方法用于清空缓存的ViewModel对象,在该方法中会调用ViewModel.onCleared()方法通知ViewModel对象不再被使用。

####ViewModel收到onCleared()通知。

 在前面我们提到,在HolderFragment的构造方法中调用了setRetainInstance(true);目的是为了在其所在的Activity被recreate时,HolderFragment不会被recreate,为什么要这么做呢(之前面卖的关子),那就要看下它的onDestroy()方法了。

5777390-881d260d66803dd6.png

在onDestroy()方法中调用了ViewModelStore.clear()方法,我们知道在该方法中会调用ViewModel的onCleared()方法。在你看了HolderFragment源码后,或许你会有个疑问,mViewModelStore保存的ViewModel对象是在哪里添加的呢? 细心的话,你会发现在ViewModelProvider的构造方法中,已经将HolderFragment中的ViwModelStore对象mViewModelStore的引用传递给了ViewModelProvider中的mViewModelStore,而在ViewModelProvider.get()方法中会向mViewModelStore添加ViewModel对象。

代码链接 提取码 39dh

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值