Android 数据绑定库有任何重大错误或问题吗?

Any significant bug or issues with Android Databinding Library?

任何使用 android 数据绑定库 (com.android.databinding:dataBinder) 的人都可以评论这个 beta 库吗? 在 android 开发者网站上说 "It might contain bugs, and it might not work for your use case, so use it at your own risk. " ,所以有任何问题或重大错误或关于它的错误吗?

过去几周我一直在研究数据绑定库,考虑到它是第一个版本,它非常强大。

到目前为止我发现的唯一错误有解决方法。我会在下面解释。

在数据绑定布局文件(现在使用 <layout> 标签作为根的布局文件)中使用 <include> 标签时,生成的代码会为 <include> 标签的父 ViewGroup。

使用 DataBindingUtil 扩充视图时,应用程序将在尝试解析 ViewGroup 时崩溃并显示 a。代码生成器和运行时绑定逻辑之间似乎存在不同的行为。

问题示例

这是一个存在上述问题的布局示例。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
    </data>

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

        <!-- This include causes no issues -->
        <include
            layout="@layout/view_content"/>

        <ScrollView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent">

            <!-- This include however causes the data binding to crash on the ScrollView -->
            <include
                layout="@layout/view_content"/>

        </ScrollView>

    </RelativeLayout>
</layout>

这是包含的布局。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
    </data>

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Test"/>
</layout>

尝试使用 DataBindingUtil.setContentView 时发生以下崩溃。

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ScrollView.setTag(java.lang.Object)' on a null object reference

解决方案(解决方法)

我找到的临时解决方法是将虚拟值绑定到 <include> 标记的父 ViewGroup。这允许数据绑定器在运行时找到 ViewGroup,避免崩溃。

下面是实际修复的示例:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="viewModel"
            type="com.example.ViewModel"/>
    </data>

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

        <!-- This include causes no issues -->
        <include
            layout="@layout/view_content"/>

        <ScrollView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            bind:visibility="@{viewModel.dummyVisibility}">

            <!-- This include will not cause a problem now that the ScrollView has a value being bound -->
            <include
                layout="@layout/view_content"/>

        </ScrollView>

    </RelativeLayout>
</layout>

这是最基本的视图模型:

package com.example;

import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.view.View;

public class ViewModel extends BaseObservable {
    @Bindable
    public int getDummyVisibility() {
        // TODO: This is a work around. Currently data binding crashes on certain views if they don't have binding.
        return View.VISIBLE;
    }
}

希望这个问题在未来得到解决,不再需要此解决方法!

编辑

我在 https://code.google.com/p/android-developer-preview/issues/detail?id=2421

上发现了另一个关于自定义绑定适配器的问题

我发现了一个关于 ObservableField 的错误,已提交 here

根据 databinding guide,模型 class 可能包含 ObservableField 属性以使其属性定义简短。指南中的示例:

private static class User extends BaseObservable {
    public final ObservableField<String> firstName =
       new ObservableField<>();
    public final ObservableField<String> lastName =
       new ObservableField<>();
    public final ObservableInt age = new ObservableInt();
}

通过使用此 class,不会生成 class BR 中的名称属性,并且无法编译实现 classes,因为它们引用了它们。例外:这些属性在布局 XML 中也被定义为变量,而在指南的示例布局中并非如此:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>
</layout>

BR.user 已定义; BR.firstName、BR.lastName 和 BR.age 不是。

像这样更改用户 class 解决了问题:

private static class User extends BaseObservable {
    @Bindable
    public final ObservableField<String> firstName =
       new ObservableField<>();
    @Bindable
    public final ObservableField<String> lastName =
       new ObservableField<>();
    @Bindable
    public final ObservableInt age = new ObservableInt();
}

在我看来,当使用 Observable* 类型时,它们的名称应该默认添加到 BR.java 并且 @Bindable 应该被废弃。

至少应该通过向 ObservableField 示例代码添加 @Bindable 注释来修复该指南。

我花了两天时间分析并解决了这个问题。

目前不支持@style。例如:

style="@{viewModel.getStyle() ?? @style/default_style}"

如果 getStyle() 返回 null,以上将尝试设置在样式资源中找到的默认样式。不幸的是,这是不可能的,但可以为数据绑定 imo 提供非常强大的补充。

当我在 Android 上使用数据绑定库实现 MVVM 模式时,我发现了几个错误。这里已经提到了其中一些。虽然,我可以添加另一个与事件相关的错误。事件不能包含参数。这意味着数据绑定库在发送事件时不具备传输一些额外信息的重要能力。但是,我希望在下一个版本中它会得到修复。在此之前,请在此处查看此问题的解决方案:https://code.google.com/p/android/issues/detail?id=185097