Android下MVVM-Databinding框架
引言
MVVM和Databinding是当前非常流行且实用的框架和工具,相信大家已经对MVC、MVP、MVVM框架以及Databinding有一定的了解。本篇文章主要介绍一个基于Databinding封装的MVVM框架,并且结合当下非常火的Android Architecture Components库可以实现一种更简单的MVVM框架。
一、MVVM-Databinding框架背景
开篇先来了解下MVVM和Databinding及其各自优势,才能说明我们为什么要不辞辛劳的合二为一,并希望最终达到1+1>2的效果。
1. MVVM框架简介及优势
MVVM框架类似于早期的MVC和近期的MVP,但是比起这两个更为强势。MVVM中的ViewModel层类似MVP的Presenter层,所需要做的完全就是跟逻辑相关的代码,完全不会涉及到UI。当数据变化,直接驱动UI的改变,中间省去了MVP中冗余的接口。同时,在ViewModel层编写代码中,要求开发者需要将每个方法尽可能的做的功能单一,不与外部有任何的引用或者是联系,无形中提高了代码的健壮性,方便了后期的单元测试。如果想进一步了解的话,可以看下我同事写的文章(https://mp.weixin.qq.com/s/qk9vJcPgcVPYDn2rTTCJ1A)。下图形象说明了MVC、MVP、MVVM的进化过程:
2. 数据绑定框架DataBinding简介及优势
DataBinding是谷歌出台的工具,可以实现UI和数据的绑定。我们的View和ViewModel通过DataBinding可以实现单向绑定或双向绑定,做到UI和数据的相互监听。同时开发者的任务分配也就很明确了,负责ViewModel的小伙伴完全不用考虑UI如何实现,很大程度上提高了代码的开发效率和后期出问题跟踪的准确性,针对这些好处,采用MVVM进行代码开发还是非常有必要的。当然,实现MVVM框架不是只能用DataBinding,可以实现UI和数据绑定的框架都可以,像开源框架RoboBinding等,甚至也可以使用Android Architecture Components库中的Livedata实现手动绑定UI和数据。如果您对Databinding不太熟悉,可以看下我写的这篇入门文章(https://www.jianshu.com/p/9fb720f405a7)。下图描述了Databinding数据流向:
3. MVVM-DataBinding框架简介
谷歌提供了一个Databinding工具,为什么我们要重新定义一套框架呢?其实在我们使用Databinding的过程中可以发现,单纯的使用Databinding使用有几个痛点: 1.Databinding规则比较多,而且每个人对其理解不同,用起来比较杂乱,导致代码阅读性差;2.对于已经使用MVP框架的开发者来说,希望进化到MVVM更顺滑,那要是能把Presenter改成ViewModel就能完成转化简直太开心了。3.实现Databinding数据和UI绑定的方法(如使用DatabindingUtil)较为机械且繁琐。如今并没有一个好的框架能规避以上问题,于是我将DataBinding工具和MVVM框架进行了封装,希望能解决以上问题并融合两者的优点,最终达到1+1>2的效果,下面将讲述是如何做到的。库代码可以移步我的github地址(https://github.com/ganlinux/JDMvvmDatabinding.git)。
二、MVVM-Databinding框架详解
下面我将通过三部分介绍我的MVVM-Databinding框架:1.如何使用本框架2.本框架的实现过程。3.本框架的优点及可能的隐患。
1、如何使用本框架
通常我们开发过程中会在Activity、Fragment、楼层、列表item中使用UI控件,所以我们的框架也将支持这几种情况的数据绑定。俗话说“无规矩不成方圆”,我们通过定义少量规则来避免使用Databinding中的大量规则,进而提高代码可读性。以下是我们约定的五点规则:
① 我们约定把Activity、Fragment绑定的数据放在xxxViewModel中,楼层绑定的数据放在楼层类(代码中的xxxFloor)中,列表item绑定的数据放在ViewHolder中。就是说要绑定的数据都会放在其处理逻辑的类中。
② Activity和Fragment的布局文件要做些改变,要绑定的数据类名称(如xxxViewModel)使用"viewModel",其类型为要绑定数据的全路径类名。如下图示例:
③ 楼层的布局文件中引用的数据类使用名称为"floor",类型也是要绑定数据的全路径类名,如下图示例:
④ 列表的item布局文件中引用的数据类使用名称为"item",其类型也是要绑定数据的全路径类名。如下图示例:
⑤ 数据的控制逻辑尽量写在处理逻辑的类中,不要写在布局文件中,写在布局文件中会给排查问题造成困难并且造成他人阅读代码困难。
以上是本框架制定的简单规则,剩下的工作基本上跟接入MVP框架相似,与MVP不同的就是我们会修改相应的布局文件达到UI和数据绑定的目的。以下是接入框架的步骤:
1)创建ViewModel/楼层/ViewHolder继承自BaseViewModel/BaseFloorView/BindingViewHolder,里面可以创建要绑定的数据,例如下图:
2)创建layout文件,按照之前的规则配置要绑定的数据。如指定生成的绑定类名为“DemoNormalFragmentBinding”,名称为"viewModel",其类型为第一步的ViewModel的全路径,绑定布局和控件例如“android:text="@{viewModel.pageTitle}"”,如图:
3)创建Activity、Fragment并继承BaseBindingMvvmActivity或BaseBindingMvvmFragment,继承时需要指定绑定的ViewModel类和Binding类,这一步就完成了数据和布局UI的绑定。楼层和列表item需要完成的是ViewHolder中数据和布局UI的绑定。例如:
完成了上面这三步,就已经完成了布局和数据的绑定,你需要做的就只是写数据变化的逻辑了,只要数据改变对应的UI就会改变了。
是不是很神奇?比MVP更简洁吧?
2、本框架的实现过程
先来看下框架的代码结构,如图:
代码一共分四部分,第一部分为公共的ViewBindingAdapter,主要功能是实现公共自定义控件的绑定注入。第二部分为Activity、Fragment、Viewmodel相关基类。第三部分为楼层相关基类。第四部分为Recyclerview列表相关基类。
这里以楼层框架为例,进行相关实现机制的讲解:
1)创建布局文件,生成BR文件占位资源。BR文件是Databinding编译期间生成的,跟R文件类似,主要作用是当数据改变后,可以用该标识符通知 DataBinding,用新的数据去更新UI。这里占位的目的是在SDK中生成一个通用的名称,在其他引用SDK的项目中引用。
2)实现绑定Binding类并设置数据,就是这个步骤省去了开发者手动设置绑定的工作。其实就是调用了Databinding的通用绑定方法,这样就不用开发者显示的调用这个方法了。
3)由于楼层就是一个Recyclerview的列表组成的,可以继承基类Adapter ,在onBindViewHolder调用show方法,并且调用excutePendingBindings()方法触发刷新。
以上是我楼层实现MVVM-Databinding框架的过程,如果不关心实现过程的开发者可以直接看下demo就可以上手了。下图是楼层demo的实现效果:
3、本框架的优点及可能的隐患
这个框架大部分优点其实是结合了MVVM和Databinding的优点,如:
1.减少findViewbyId造成的IO操作性能损耗,这是Databinding的一条优点,因为Databinding在编译期间就将控件从布局映射到在Binding类中,只是在绑定阶段一次性实例化这些控件就直接可以使用,省去了findViewbyId的IO操作。
2.减少频繁线程切换,这条也是由Databinding的优点,你可以在任何你想要的线程上进行更新,因为Databinding是在UI线程进行响应。
3.减少逻辑代码冗余、提高编码效率,这条优点就是本框架的优势所在,也是为什么要将这两种优秀框架集合在一块的原因。
4.削减DataBinding的混乱规则,这是单纯的使用DataBinding体验较差的地方,在我们的框架里可以摒弃其复杂的规则。
5.更简单的单元测试,这是MVVM框架的优点,由于其天然的低耦合性,可以使我们单元测试更简单。
可能的隐患:
1.由于DataBinding在xml提供了丰富的操作符,但是由于Android studio天生的xml语法检查的贫弱,xml布局中的表达式逻辑错误,不能准确定位,导致debug难度增加,事实上一些BindingAdapter的错误在build的时候也会被提示xml错误。我在开发过程中遇到过几次,常常需要重启Android studio才能消除问题,这个问题只能寄希望于谷歌在其IDE上解决。
2.由于DataBinding是在预编译时期生成一些布局和代码,这可能导致许多需要动态加载资源或代码的工程(比如插件化工程)需要做些改变。当然普通的APP开发使用这个框架是没问题的。
当然如果你按照以上步骤改造了自己的APP,可能过程中还会遇到一个问题,就是ViewModel中上下文引用的问题,当然如果能用应用级别的context当然最好,但是有时候我们必须用activity的context时可能就有些困难了,因为我们不推荐ViewModel持有context,这样做可能造成内存泄漏,这时候我们可以结合Android Architecture Components来实现context的获取,在我的github的工程中有两个分支,其中mvvm-databinding-aac分支就是结合了Android Architecture Components来实现的。
三、结语
数据绑定的应用软件开发是一种趋势,使用DataBinding的优点显而易见。在我们选择框架的过程中需要考虑诸多问题,比如性能问题、使用便捷程度、单元测试、是否相互独立等。当然还有一点很重要的就是,共同开发的同事是否认同并喜欢这种开发方式,毕竟这会影响到他们的开发流程,习惯和体验。如何说服他们认可这种框架,就需要你提供便捷和健壮的封装框架,使他们很爽很开心很便捷地使用这种框架,并且能够满足项目代码的可扩展性,解耦和相关独立。做到这些,就能满足调用者,项目代码健壮性,模块独立性和可扩展性的需要了。我以上讲述算是抛转引玉吧,希望能共同完善框架,共同进步。
github地址:https://github.com/ganlinux/JDMvvmDatabinding.git