Android架构Android开发

基于DataBinding与LiveData的MVVM实践

2019-06-05  本文已影响41人  PenguinMan

前言

在MVP模式下,随着业务逻辑的不断增加,UI也会变得越来的多样化,UI改变多样化势必会造成View层的接口case变多,View的接口类就会变得异常庞大。MVVM的方式完美的解决了MVP模式的这一痛点,它采用双向绑定的机制,实现了UI与数据的绑定操作。仅通过改变数据就可以实现对UI的更新操作。


view与数据.png

DataBinding

MVVM在前端领域可谓是如鱼得水,很多著名的框架都是采用的MVVM模式,最近大火的VUE就是其中之一,而在安卓领域,由于一直一来xml的layout都是一个比较功能弱化的存在,所以实现MVVM双向绑定就变得比较困难,直到谷歌推出了实现视图和数据双向绑定的工具DataBinding,才让双向绑定变得操作简单。

初始化

在项目中开启DataBinding只需要在gradle的android下新增以下代码即可:

    dataBinding {
        enabled = true
    }

在xml的编写上,DataBinding有新的写法,最外层不再是页面的根布局,而是layout,layout中包含有生命的绑定class以及根布局。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <!--绑定数据类-->
        <variable name="weatherData"
            type="com.yanghaoyi.klivedata.model.bean.WeatherNetData"/>
        <!--绑定逻辑类-->
        <variable
            name="presenter"
            type="com.yanghaoyi.klivedata.view.MainActivity.WeatherPresenter"/>
    </data>
    <!--页面根布局-->
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </LinearLayout>
</layout>

编写完xml文件后需要rebuild一下工程,rebuild成功后会自动生成继承ViewDataBinding的绑定Class类,是通过layout文件进行命名的,例如activity_main.xml生成的绑定class为ActivityMainBinding。ActivityMainBinding需要在onCreate函数中通过setContentView进行初始化。

  lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //初始化DataBinding
        binding =DataBindingUtil.setContentView(this,R.layout.activity_main)
        binding.presenter = WeatherPresenter()
        init()
    }

建立双向绑定

建立了绑定机制后在xml文件中即可通过使用@{}实现View与Data的双向绑定,以TextView为例,当weatherData.data.ganmao放生改变的时候,tvCold文本组件会自动更新文本显示。??相当于?:表达式,即当weatherData.data.ganmao数据为null的时候,文本组件取值为 ' 详情 '。

        <TextView
            android:id="@+id/tvCold"
            style="@style/Common_Button"
            android:text="@{weatherData.data.ganmao??`详情`}"/>

除了简单的数据绑定,DataBinding还可以为我们实现事件的绑定,以点击按钮从EditText获取文字查询为例,首先定义事件Presenter绑定,binding.presenter = WeatherPresenter()并在xml中建立variable对应关系,WeatherPresenter内部逻辑非常简单,如下:

    /**  点击事件逻辑中心 **/
    open inner class WeatherPresenter{
        open  fun onClick(view:View,city:String){
            when (view.id) {
                R.id.tvSearch -> {
                    this@MainActivity.viewModel.requestWeather(city)
                }
            }
        }
    }

WeatherPresenter的onClick函数需要两个参数,一个是被点击的view,一个是字符串类型的城市名称,在xml中我们只需要@{(view)->presenter.onClick(view,edInput.text.toString)}即可实现tvSearch控件与WeatherPresenter的绑定。

        <EditText
            android:id="@+id/edInput"
            android:hint="@string/please_input_city"
            style="@style/Common_EditText"/>
        <View
            style="@style/Common_CutLine"/>
        <TextView
            android:id="@+id/tvSearch"
            style="@style/Common_Button"
            android:text="@string/search"
            android:onClick="@{(view)->presenter.onClick(view,edInput.text.toString)}"/>
  

LiveData

LiveData 是一个能够感知 Activity 与 Fragment生命周期的数据容器。当 LiveData 所持有的数据改变时,它会通知相应的界面代码进行更新。LiveData 持有界面代码 Lifecycle 的引用,只有在界面生命周期处于 Started 或 Resumed状态时才会更新数据。当绑定对象销毁,LiveData会自行清理。

结合ViewModel

ViewModel是业务逻辑的控制中心,当数据Data放生改变的时候,viewModel更新LiveData的value值,由LiveData的双向绑定机制通知UI更新。

    /**  添加viewModel绑定 **/
    private fun init(){
        viewModel = ViewModelProvider(
                this, ViewModelProvider.AndroidViewModelFactory(application)
        ).get(WeatherViewModel::class.java)
        //添加数据观察
        viewModel.weatherEvent.observe(this, Observer<WeatherNetData> { data -> binding.weatherData = data })
    }

在LiveData中并没有对外暴露的公共方法进行更新数据,可以使用MutableLiveData进行数据的更新,分别是setValue和postValue,他们之间的区别在于setValue是必须是在主线程的,而postValue可以是在子线程中使用。

     /**  LiveData初始化 **/
    var weatherEvent = MutableLiveData<WeatherNetData>()

    /**  获取天气数据 **/
    open fun requestWeather(city:String){
        WeatherModel().request(object :OnGetDataListener<WeatherNetData>{
            override fun fail(response: WeatherNetData?, msg: String?) {
                //数据更新
                //主线程
                weatherEvent.value = response
                //子线程
                weatherEvent.postValue(response)
            }
            override fun success(response: WeatherNetData?) {
                //数据更新
                weatherEvent.value = response
            }
        },city)
    }

MVVM实例

为了更好的展示DataBinding与LiveData实现MVVM的模式,这里用一个简单小例子来辅助说明一下。


Demo示例

首先我们来明确一下Demo的功能,这是一个通过输入城市名查询城市天气的简单应用,在MVVM划分中,Model层应当包含网络请求以及网络请求的数据Bean,View层应当包括一个EditText控件和两个TextView控件,ViewModel中通过LiveData实现数据与视图的双向绑定。


MVVM.png

Model

Model层内部是一个网络请求,通过接口回调将请求结果返回给ViewModel,如下:

/**
 * @author : YangHaoYi on  2019/6/4.
 * Email  :  yang.haoyi@qq.com
 * Description : 数据Model
 * Change : YangHaoYi on  2019/6/4.
 * Version : V 1.0
 */
class WeatherModel : AbstractBaseModel() {

    /**  天气网络请求 **/
    fun request(listener:OnGetDataListener<WeatherNetData>,city:String){
        val response = api.getWeatherData(city)
        response.observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(object :Observer<WeatherNetData>{
                    override fun onError(e: Throwable?) {
                        listener.fail(null,"")
                    }
                    override fun onNext(t: WeatherNetData?) {
                        listener.success(t)
                    }
                    override fun onCompleted() {
                    }
                })
    }
}

ViewModel

ViewModel层内部通过LiveData实现对View数据改变的通知

open class WeatherViewModel : ViewModel(){

    var weatherEvent = MutableLiveData<WeatherNetData>()

    /**  获取天气数据 **/
    open fun requestWeather(city:String){
        WeatherModel().request(object :OnGetDataListener<WeatherNetData>{
            override fun fail(response: WeatherNetData?, msg: String?) {
                //主线程
                weatherEvent.value = response
            }
            override fun success(response: WeatherNetData?) {
                weatherEvent.value = response
            }
        },city)
    }
}

View

View中收到更新状态后通过DataBinding实现对UI的更新

class MainActivity : FragmentActivity() {

    lateinit var binding: ActivityMainBinding
    lateinit var viewModel:WeatherViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //初始化DataBinding
        binding =DataBindingUtil.setContentView(this,R.layout.activity_main)
        binding.presenter = WeatherPresenter()
        init()
    }

    /**  添加viewModel绑定 **/
    private fun init(){
        viewModel = ViewModelProvider(
                this, ViewModelProvider.AndroidViewModelFactory(application)
        ).get(WeatherViewModel::class.java)
        //添加数据观察
        viewModel.weatherEvent.observe(this, Observer<WeatherNetData> { data -> binding.weatherData = data })
    }
}

示例源码

文章对通过DataBinding与LiveData实现的MVVM功能展示Demo已上传至GitHub,感兴趣的朋友可以clone下来共同探讨学习一下。
GitHub源码:Java版本链接

GitHub源码:Kotlin版本链接

上一篇下一篇

猜你喜欢

热点阅读