美文网首页
架构组件专栏 | ViewModel深入浅出

架构组件专栏 | ViewModel深入浅出

作者: 钟离四郎 | 来源:发表于2019-06-24 13:12 被阅读0次

    本文是架构组件专栏的开篇文章,因此在文章开头我打算花些笔墨谈谈什么是架构组件以及我为什么打算写这个专栏。

    谷歌官方为了帮助开发者加速开发并构建高质量的应用,推出了Jetpack。正如上图你所看到的,Jetpack包含了四个库,而架构组件只是其中之一。架构组件具体能做什么我将会在本专栏接下来的文章陆续与诸位分享。

    在学习架构组件并研读其中源码的过程中,我发现谷歌代码设计真的很巧妙,让人惊喜。之所以写这个专栏,一方面因为我本人打算构建这方面知识体系,另一方面希望将自己学习中的收获与诸君分享。

    下文我将引领大家开启架构组件之旅,陪伴大家从浅到深一步一步学习。

    ViewModel是什么

    ViewModel是用来提供和管理UI数据的组件,它具有以下特点:

    1.系统配置变更后依然存活

    我们之前在开发中经常会遇到比如说屏幕旋转Activity重新创建导致屏幕数据丢失的情况。通常的做法是在onSaveInstanceState方法中保存数据,在onRestoreInstanceState方法中恢复数据。这种做法有个明显不足的地方——保存的数据有限,而ViewModel则不存在这个问题,它作为一个对象,可以在配置变更后继续存活,那么它自身原有挂载的大量数据就自然而然得以保存。

    2.职责单一性

    关于这一点其实也很好理解,从上文对ViewModel的定义的解释就可以看出其职责的单一性。ViewModel只负责提供管理UI数据,这样我们就把原来在Activity/Fragment的这部分代码分离出来放到ViewModel,Activity/Fragment代码更加清爽,Activity真正做到“不在其位不谋其政”。总之,View Model 帮助代码责任划分,遵循单一职责原则。

    如何使用

    1.创建ViewModel对象

    实现一个类继承viewmodel抽象类,如下:

    如上图实现一个类继承viewmodel抽象类,

    如上图第6行,先获取一个ViewModelProvider对象,然后再调用get方法获取得到ViewModel对象,之后你便可以利用这个对象存储相关的UI数据,完整的实例在文末会给出源码地址。

    2.自定义ViewModel构造函数

    ViewModel默认构造函数是没有参数的,创建有参的ViewModel需要通过ViewModelFactory来创建,如下:

    上图展示一个需要参数的ViewModel,需要自定义ViewModelFactory如下:

    创建ViewModelFactory需要继承NewInstanceFactory并重写onCreat方法,总体上代码还是比较简单的,这里不做详细解释,接下来便可以创建ViewModel对象:

    3.用于fragment间数据通信

    同属于同一个宿主Activity的Fragment是很容易获得宿主Activity的引用,自然而然地宿主Activity的ViewModel也被共享,通过这个共享的ViewModel实现数据通信是很容易实现的。

    4.与LiveData搭配使用

    ViewMode与生命周期架构组件的另外一个类LiveData搭配使用可以组成响应式的UI,本文不对此用法展开讲解,将放在后续LiveData章节之后一并讨论。

    注意事项

    1.避免在viewmodel实现过多逻辑

    上文我们提到viewModel的职责是提供和处理ui数据,ViewModel从数据层获取的数据往往是泛化的,一般需要加工处理后返回给使用者,这一过程就是数据的处理。比如从数据层获取了一个时间形如“2019-06-20”,但是UI层要求的数据格式是“2019年06月20日”,这时就需要我们对数据做处理,但是这种操作做多了,ViewModel也会慢慢变臃肿。改进这种局面的办法有几个,首先很容易想到将一个臃肿的ViewModel分割成几个ViewModel,实际上这种方法并不是很好;另外一种办法是将ViewModel处理数据逻辑抽离封装成一个Presenter类,相对于前一种办法这种做法更值得推荐,因为它针对了ViewModel责任做粒度更细的区分——把提供UI数据与处理UI数据看做两种责任进行分离,这种做法在单一职责上做更加精细化的控制。最后我还想提一下解决这一情况的另外一种方案——CleanArchitecture架构,这里我不打算对CleanArchitecture展开解释,因为本篇文章的主题是ViewModel,但是我会在后续章节对此展开讨论。

    2.不能持有Activity/Fragment/View的Context

    关于这一点其实是好理解的,如果允许ViewModel持有Activity引用,那么在屏幕旋转重新创建时,原先的活动依然被存活ViewModel持有,导致泄露不能回收。但是开发过程中如果真的需要一个Context,可以继承AndroidVieModel,提供一个Application级别的Context。

    3.别想着取代onSaveInstanceState方法

    ViewModel能在系统配置发生变更以后依然能够存活,但是在一些极端情况下比如内存不足导致页面被系统杀死,ViewModel同样会被回收,不能够再存活。重新创建Activity时,ViewModel则会重新创建;而onSaveInstanceState则不同,不管系统回收还是配置变更都可以保存数据。所以总结来说,ViewModel与onSaveInstanceState是共存互补关系,而并非取代关系。ViewModel的特点在于保存大数据,onSaveInstanceState则在于保存的时机。

    源码分析

    这里源码分析我选择的入口ViewModelProviders.of方法,如下:

    如上图第6行,ViewModelProvider分别会持有两个引用,一个是ViewModelStore,一个是Factory。这里ViewModelStore解决的是ViewModel对象存储问题,而Factory解决的是ViewModel对象生成问题,ViewModelProvider持有两个引用让其本身具备有解决ViewModel“生存”(生成、存储)能力。

    接下去看到第7行的ViewModelStores.of(activity),作用是生成ViewModel对象:

    看到第5行真正创建ViewModelStore的是holderFragmentFor(activity).getViewModelStore()

    这部分代码实在一个HolderFragment中,如下:

    第13行HolderFragment持有一个ViewModel引用

    第15行HolFragment构造函数,设置了setRetainInstance为true,这样屏幕旋转等系统配置变更Activity销毁时,Fragment不会被销毁。

    第46行,通过sHolderFragmentManager对象的holderFragmentFor方法获取 HolderFragment,sHolderFragmentManager对象是HolderFragmentManager类型,HolderFragment的内部类,见第59行。

    第119行,holderFragmentFor方法获取HolderFragment,首先通过FragmentManager尝试获取Fragment,如果存在直接返回,不存在则从mNotCommittedActivityHolders这个Map对象查找,这个Map作用是保存那些创建但还来的及提交的Fragment避免重复创建。如果上面两步都不满足则执行第130~135行创建并保存HolderFragment

    小结

    这篇文章我先介绍了ViewModel是什么的问题,接着谈了ViewModel的使用问题,包括对象的获取、自定义View ModelFractory获取有参的ViewModel、如何在Fragment间数据通信,然后又提到了viewModel使用需要注意的几点,最后对ViewModel的源码关键部分做了分析。

    相关文章

      网友评论

          本文标题:架构组件专栏 | ViewModel深入浅出

          本文链接:https://www.haomeiwen.com/subject/ldpgqctx.html