Android mvvm 之 databinding 原理

本文深入解析Android中的MVVM架构组件Data Binding的工作原理,包括rebind、observe data和observe view三种行为模式。通过分析谷歌sunflower应用的改造示例,展示了Data Binding如何实现数据和视图的绑定,以及如何免去findViewById操作,提升开发效率和代码可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文为原创内容,转载请注明出处——小舍

目录

  • 1 Data Binding 的结构
    • 1.1 rebind 行为
    • 1.2 observe data 行为
    • 1.3 observe view 行为
  • 2 样例分析——谷歌 sunflower 的改造
    • 2.1 Data——FakeData
    • 2.2 View——fragment_plant_detail.xml
    • 2.3 ViewDataBinding——FragmentPlantDetailBinding
    • 2.4 adapter 绑定说明
  • 3 免 findViewById 的实现


1 Data Binding 的结构

作为在 Android 开发中体现 MVVM 架构思想的 Data Binding,其核心是 观察者模式 的特定实现。首先,它有三个主要的实体:

  1. Data:与 View 相关的数据,它可以是 View 的可观察者对象;
  2. View:展示给用户的视图,如果有交互功能且能更新数据,它可以是 Data 的可观察者对象;
  3. ViewDataBinding:连接 Data 和 View 的中介,当 Data 或 View 作为可观察者对象时,它充当可观察者对象的代理。假如当我们写了一个名为 demo.xml 的 Data Binding 的 layout 文件后,编译工具会生成一个相应的类——DemoBinding,它的原型就是 ViewDataBinding。我们通常通过 DataBindingUtil.inflate(inflater, R.layout.demo, container, false)来实例化的 DemoBinding 对象,即 ViewDataBinding。

其次,它主要提供了以下三个方面的功能:

  1. 将特定的 View 与特定的 Data 进行绑定,便于模块化;
  2. View 自动感知和响应 Data 的变化,使得处理数据的业务层不必关心 View 的状态,便于解耦;
  3. Data 也可以自动同步带有交互功能的 View 对数据的修改,使得 UI 层的交互不必担心数据是否能同步 View 状态的问题,仍然便于解耦。

基于这三个功能,Data Binding 的结构也可以划分为三个行为模式,以下一一介绍:

1.1 rebind 行为

首先,Data 往往是一个数据的集合,数据绑定的第一步就是要将整个 Data 集合绑定到 View,比如初始化和数据的整体更新,如下图所示:

在这里插入图片描述

可以观察到,rebind 的过程就是一个简单的赋值操作,将 View 的值设置为 Data,只不过由 ViewDataBinding 这个代理来完成这个工作。图中的 _all参数表示将 View 的所有需要更新的节点都设置为 Data 的所有对应的成员值。当我们在 layout 文件中进行如下设置时,ViewDataBinding 将代理完成 View 中所有数据绑定节点的 data rebind 操作:

<data>
    <variable
        name="demoData"
        type="com.sample.DemoData" />
</data>
...

<!-- 将 DemoData.element1 绑定到 ChildView1 -->
<ChildView1
    android:id="..."
    android:width="..."
    android:height="..."
    app:element1="@{demoData.element1}"/>
...

<!-- 将 DemoData.element2 绑定到 ChildView2 -->
<ChildView2
    android:id="..."
    android:width="..."
    android:height="..."
    android:text="@{demoData.element2}"/>
...
1.2 observe data 行为

有时候,我们并不需要每次更新整个 Data 集合,而只需要更新集合中的某一个成员。我们希望看到的结果是,当 Data.element_i 发生变化的时候,View.child_i 更新就可以了,而不需要将 View 的所有视图节点都重新渲染一遍。要做到这一点,我们必须要让 View 可以观察 Data 的行为。换句话说,Data 是一个可观察者对象——这是 Data Binding 中另一个魅力所在,其行为模式如下:

在这里插入图片描述

我们可以将任何数据作为一个 Observable,然后将 ViewDataBinding 作为 View 的代理观察者,订阅 Data 的成员变化,一旦 Data 成员变化,便通知所有观察者对象——即 ViewDataBinding,然后 ViewDataBinding 再将 View 的相应节点的值设置为 Data 相应成员的新值——即图中的 _member参数。

在 Data Binding 框架中,将 Data 设置为 Observable 的方式类似于下面的代码:

public class DemoData extends BaseObservable {
   
    private int element;

    // 定义其它成员,省略

    @Bindable
    public int getElement() {
   
        return this.element;
    }

    public void setElement(int e) {
   
        this.element = e;
        notifyPropertyChanged(BR.element);
    }

    // 省略其它成员操作
}

这里有三个关键部分:

  1. BaseObservable:可观察者基类(实际的祖先基类是一个 Observable 接口),实现改接口后,ViewDataBinding 就会在每次 rebind 的时候去订阅 Data 的变化;
  2. @Bindable 标注:声明该成员是可被观察的,以及在 layout 中可以以 Data.xxx(标注的方法名如果为 getXXX)的方式进行访问;
  3. notifyPropertyChanged 方法:BaseObservable 用于通知具体成员发送变化的方法,只要该方法被调用,ViewDataBinding 就会检索出是哪一个 element 的变化,并只对 View 相应的节点进行更新。
1.3 observe view 行为

在开发中,根据业务需求,我们一般能遇到两种类型的 View:

  • 一种是只用于展示的 View,它只展示 UI 状态,而不反馈状态,我们称之为 单工View
  • 另一种除了展示以外,还会反馈状态给监听者,我们称之为 双工View

在 Android 的 UI事件流中,因为所有的 View 都是可以反馈状态的,所以准确来说,所有的 View 其实都是双工的。我们在这里区分单工和双工是针对业务需求的,比如:我们很多的视图只需要它们展示就可以了,不需要监听它们的状态变化,那么我们将其归为单工View。

费尽心思进行这样的划分,是因为,单工View 只需要有 observe data 行为就可以了;而双工View 往往就需要 observe view 行为。具体来说,在反馈状态时需要更新 Data 的双工 View,我们需要进行 observe view 行为。

因为双工View 会更新 Data,所以为了保证数据的一致性,Data 需要观察双工View 的状态变化。要做到这一点,这样的双工View 必须是一个可观察者对象。得益于 UI事件流的实现,双工View天然是可观察的(只要能反馈状态,就意味着能被观察)。在自定义的双工View中,可以间接引用 ViewDataBinding,这样 ViewDataBinding 就可以代理 Data 订阅 View 的状态变化:

在这里插入图片描述

图中我们可以看到,observe view 行为是伴随着 observe data 行为一起实现的。我们可以单独只实现 observe data 行为,但是如果要实现 observe view,必须同时实现 observe data 行为,因为该双工View 本身是也是要绑定 Data 的,也需要观察 Data 的变化——即 observe view 行为的实现需要通过双向绑定来达到,View 和 Data 同时都是对方的可观察者对象。

Data Binding 的原理基本上都在这三种行为模式的图解上,为了更深入一些实现的细节,我们下面通过一个样例来分析一下。



2 样例分析——谷歌 sunflower 的改造

sunflower app 是谷歌推出的 jetpack 库应用的最佳实践,官方只提供了 kotlin 的版本,如果大家更想看 java 版本,我改写了一个,文末放送。

我们通过 sunflower 中的一个植物种植详情页面作为具体的案例分析。为了说明 observe view 行为,我对原来的实现做了一些修改,具体不表,大家直接看现有修改后的代码分析就可以了。另外,代码也不会全贴,只贴出少量关键代码便于大家抓住重点理解。

2.1 Data——FakeData

我们定义了一个数据类——FakeData,其中有两个成员:

  • viewModel:植物的详细信息,用于 data observe 行为分析;
  • name: 一个测试字段,表示页面标题,用于 view observe 行为分析;

其代码如下:

public class FakeData extends BaseObservable {
   
    private PlantDetailViewModel viewModel;
    private String name;

    public void setViewModel(PlantDetailViewModel viewModel) {
   
        this.viewModel = viewModel;

        // 同时通知 plant 和 isPlanted 的变化,因为这两个成员被保存在组合成员 viewModel 里
        notifyPropertyChanged(BR.plant);
        notifyPropertyChanged(BR.isPlanted);
    }

    @Bindable
    public Plant getPlant() {
   
        return viewModel != null ? viewModel.getPlant() : null;
    }

    @Bindable
    public Boolean getIsPlanted() {
   
        return viewModel != null ? viewModel.getIsPlanted() : false;
    }

    public void setName(String name) {
   
        this.name = name;

        // 通知 ViewDataBinding,name 成员变化了
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public String getName() {
   
        return name;
    }
}

这里,FakeData 暴露给外面的成员实际是三个:plant,isPlanted,name。其中 plant 和 isPlanted 由 viewModel 提供,所以当更新 viewModel 时,必须同时通知 ViewDataBinding 两个属性的更新。

2.2 View——fragment_plant_detail.xml

植物详情页的布局文件便是我们的 View 定义,我们只关注它与 FakeData 绑定的部分,且只列出部分节点:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <!-- rebind -->
        <variable
            name="fakeData"
            type="com.google.samples.apps.sunflower.data.FakeData" />

        <!-- 其它Data声明 -->
        ......
    </data>

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        ...>
    ......

            <!-- ① 绑定:observe data(赋值绑定) -->
            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:id="@+id/toolbar_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                app:contentScrim="?attr/colorSurface"
                app:statusBarScrim="?attr/colorSurface"
                app:collapsedTitleGravity="center"
                app:collapsedTitleTextAppearance="@style/TextAppearance.Sunflower.Toolbar.Text"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:title="@{fakeData.name}"
                app:titleEnabled="false
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值