MVVM陷阱之DataBinding(数据绑定库)

2020-05-27  本文已影响0人  Codyer

本文已经对《第一行代码》作者郭霖的公众号授权独家发布

一、什么是DataBinding?

官方文档的描述如下:

数据绑定库是一种支持库,借助该库,您可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源。

布局通常是使用调用界面框架方法的代码在 Activity 中定义的。例如,以下代码调用 findViewById() 来查找 TextView 微件并将其绑定到 viewModel 变量的 userName 属性:

 TextView textView = findViewById(R.id.sample_text);
 textView.setText(viewModel.getUserName());

以下示例展示了如何在布局文件中使用数据绑定库将文本直接分配到微件。这样就无需调用上述任何 Java 代码。请注意赋值表达式中 @{} 语法的使用:

<TextView
        android:text="@{viewmodel.userName}" />

借助布局文件中的绑定组件,您可以移除 Activity 中的许多界面框架调用,使其维护起来更简单、方便。还可以提高应用性能,并且有助于防止内存泄漏以及避免发生 Null 指针异常。


针对以上两点,从上面的引用中可以看出,官方明确说了是可以提高应用的性能,并且会使项目维护起来更方便。

二、哪些情况下我可以或者应该用DataBinding?

在使用数据绑定之前你要清楚你要拿它来解决什么问题?

  1. 如果只是为了替代findViewById()那你可以去使用最新抽取出来的视图绑定(ViewBinding)因为这个相对于数据绑定来说更轻巧,在DataBinding刚出来的时候并没有单独区分数据 or 视图绑定,这是在新的版本中,为了更好的设计,将ViewBinding进行了单独的抽取。
    从最新版本的代码可以看出:

      public abstract class ViewDataBinding extends BaseObservable 
      implements ViewBinding {}
    

    抽象类ViewDataBinding实现了ViewBinding接口,这个名字也是取得恰到好处,View-Data-Binding,其实大家常说的DataBinding是包含ViewBinding的。
    说句题外话,如果只是为了替代findViewById(),并且是使用kotlin开发的话,也可以考虑Kotlin-android-extensionKAE具有和ViewBinding差不多的功能,具体使用方式在此也不介绍了,有兴趣的可以去找相关资料。`

  2. 为了实现数据绑定,类似数据驱动架构,MVVM架构等。恭喜你,这时候Databinding是不错的选择。
    说到这里又涉及到MVCMVPMVVM设计架构的区别,相信大家对这几个概念都有所了解。
    首先,明确一点,架构的目的就是为了提高开发效率,降低维护成本。
    利用面向对象的设计原则,对每个模块的职责进行合理的划分,为了让其他人更好的理解架构设计思想,然后给予每个模块一个通用的名词解释,为了更好的说清楚数据绑定,这里就简单解释一下目前常见的几种架构(以Android为例)。

    • MVC
      • 模型层(Model),负责处理数据逻辑,一般包含数据库、本地数据、网络获取的Bean等组成。
      • 视图层(View),负责处理视图显示,一般由XML布局承担此责任,基本组件和自定义View等充当视图层的补充元素。
      • 控制层(Control),负责处理业务逻辑,一般由ActivityFragment承担此责任。
    • MVP
      • 模型层(Model),负责处理数据逻辑,一般包含数据库、本地数据、网络获取的Bean等组成。
      • 视图层(View),负责处理视图显示,一般由XML布局承担此责任,基本组件和自定义View等充当视图层的补充元素,ActivityFragment充当视图层和控制层的粘合剂。
      • ??(Presenter),负责处理业务逻辑,由从原来MVC控制层中抽取出来的Presenter充当控制层(Presenter)。
    • MVVM
      • 模型层(Model),负责处理数据逻辑,一般包含数据库、本地数据、网络获取的Bean、(这里我单独抽取的视图数据ViewData概念也属于Model层)等组成。
      • 视图层(View),负责处理视图显示,一般由XML布局承担此责任,基本组件和自定义View等充当视图层的补充元素,ActivityFragment主要负责视图层绑定事件触发,熟练的话也可以直接在XML中绑定触发事件。
      • ??(ViewModel),通过数据绑定连接ViewModel(这里由ViewData充当视图模型被绑定到视图上)实现视图层和模型层的解藕,事件触发后通过ViewModel处理业务逻辑,并且通过数据驱动的方式修改视图数据,而达到间接修改视图的功能。注意:ViewModel一定不能持有视图层的引用,同样不能持有Context的引用!不然还是MVP!

    对于新手来说,看完上面说明,更让人觉得摸不着头脑,只是换一个名字而已,最终不还是分三层吗?视图层(View)、数据层(Model)、逻辑处理层(???),这么简单的东西,为什么搞得很高深莫测的样子?

    参考一下图示也许你就豁然开朗了:

    image
    其实不同的设计架构最终目的还是为了解耦,实现高内聚低耦合一直是架构师的理想,这种情况下,每个程序员只需要关心自己的模块就可以了。
    • 就拿MVVM来说,当一个项目足够大的时候,可能有的人负责界面绘制(XML),有的人负责业务逻辑处理(ViewModel),有的人负责数据逻辑处理(Model)。这时候,每个模块的人只需要关心自己的逻辑就可以了,而且每个模块都可以单独跑Use Case,每个模块并没有很强的依赖关系,而且当某个模块的逻辑变更了并不一定会影响到其他模块的变更。
有几点要注意:

1、官方的ViewModel库并不是实现MVVM架构的必备,MVVM的重点是解藕,通过一定方式解除ViewModel的耦合,比如使用数据绑定库DataBinding

2、也有不使用DataBinding实现的MVVM吗?其实也有,比如说郭神第三版的《第一行代码》中的方式,利用LiveData实现ViewModel的解藕,且ViewModel不依赖ViewContext,这里郭神把ActivityFragment当作View的主体,而我更倾向于把XML当作View的主体,所见即所得,看得到的当成View,会更直观一点。ActivityFragment只是当作一个粘合剂,比如进行事件绑定和一些复杂动画的处理等。所以DataBinding更多的是服务于XML这种View的。

3、ViewModel库是在DadaBinding库之后才有的,ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存,这样可以更好的提升用户体验和提高应用性能。

image
上图说明了Activity经历屏幕旋转而后结束的过程中所处的各种生命周期状态。该图还在关联的 Activity生命周期的旁边显示了ViewModel的生命周期。此图表说明了 Activity 的各种状态。这些基本状态同样适用于 Fragment 的生命周期。
4、其实可以从官方的介绍中看出来,官方的ViewModel库和我们所说的MVVM架构中的ViewModel层并不是等价的东西,ViewModel层不止包含ViewModel数据(我更愿意称为ViewData),还应该包含视图模型的逻辑处理。
总的来说,为了提高开发效率,为了更好的在大型团队中协调开发,MVVM是一个不错的选择!目前为止,个人认为DataBinding、ViewModel,再加上LiveData,是搭建MVVM架构最完美的组合。

三、DataBinding的基本使用方式

注意:即使应用模块不直接使用数据绑定,也必须为依赖于使用数据绑定的库的应用模块配置数据绑定。

目前为止,布局已经和数据进行了绑定,其实这时候和平时我们使用findViewById()然后再setText()差别并不大,精彩的是后面。

四、DataBinding的高级用法

上面虽然实现了基本的数据绑定,但是改变原来的数据对象,界面并不会发生改变,这个时候就需要引入一个新的概念,可观察字段

可观察性是指一个对象将其数据变化通知给其他对象的能力。通过数据绑定库,您可以让对象、字段或集合变为可观察。
通过数据绑定,数据对象可在其数据发生更改时通知其他对象,即监听器。可观察类有三种不同类型:对象、字段和集合
当其中一个可观察数据对象绑定到界面并且该数据对象的属性发生更改时,界面会自动更新。

其实说白了就是观察这模式的灵活运用,最终实现了数据的绑定,对于双向绑定也没有什么特别的,自动生成的代码会根据我们在布局中是否设定了双向绑定,而主动帮我们设置一个InverseBindingListener监听,XML中格式如下:android:text="@={...}"

小知识点

五、优雅的使用DataBinding实现MVVM

有问题的不是工具,而是使用工具的方式!

很多逻辑具有通用性,我们可以抽取很多模版代码作为基类使用,比如说列表的ListAdapter、数据库Room、数据差分类DiffUtil.ItemCallback、RecyclerView的ViewHolder都可以进行很好的封装,使用时就会变得很简单,以后再也不用处理那么多的AdapterViewHolder了。

因为篇幅所致,这里就不详细介绍了,有兴趣的可以参考github源码,源码里面的README也有一部分介绍。
源码传送门

GitHub地址:https://github.com/codyer/component

说明
因为component是实际开发时可能会使用到的一些组件,为了方便,我之前放在一个项目下统一管理的,因此这个项目包含很多模块,感兴趣的欢迎StarFork
如果只想看MVVM的代码可以只参考以上项目的一个modlue
MVVM-模块

GitHub地址:https://github.com/codyer/component/blob/master/app-core/README.md

至此,为DataBinding的正名就算完成了。

尾记

写这篇文章花了整整一天的时间,希望走过路过的伙伴们不要吝啬一个点赞和Star

另外,之前因为机缘巧合实现了一套基于LiveData的事件总线LiveEventBus,自己使用下来发现其实还是很不错的,之前一直做项目也不空闲,再加上觉得好像大家都可以实现,也没有什么太难的地方,也就没有想着写出来分享一下。

最近看见有人实现了一个类似的库,而且还在各种地方发表,首先很佩服他的分享精神,也希望大家多向这样的同学学习,但是呢,看了他的源码和说明,他的实现方式,感觉还是有优化的地方,而且还有些许问题,因此和这次为DataBinding正名一样,激起了我的保护欲,为了保护技术,保护大家优雅的使用姿势,决定花花时间整理一下,教教大家如何优雅的使用LiveData实现一套EventBus

如果有下一篇,它将是《如何优雅的使用LiveData实现一套EventBus

~敬请期待~
----------------------------------------------------------- by Cody.yi

谢谢阅读

写得越多发现自己不知道的越多,如有错误缺漏之处欢迎指正!

下一篇以及完成,欢迎阅读
https://www.jianshu.com/p/79d909b6f8bd

上一篇 下一篇

猜你喜欢

热点阅读