目录
[1.1 可观察字段(ObservableFields)](#1.1 可观察字段(ObservableFields))
[问题1: 为什么字段必须是 public final ?](#问题1: 为什么字段必须是 public final ?)
[问题2:set() 方法中 notifyChange() 干了什么](#问题2:set() 方法中 notifyChange() 干了什么)
[1.2 可观察字段(ObservableInt 等)](#1.2 可观察字段(ObservableInt 等))
[ObservableBoolean vs ObservableField](#ObservableBoolean vs ObservableField)
[问题1:为什么 ObservableField 不同时实现 Parcelable?](#问题1:为什么 ObservableField 不同时实现 Parcelable?)
[1.3 可观察对象 (BaseObservable)](#1.3 可观察对象 (BaseObservable))
[问题1:@Bindable 注解干了什么?](#问题1:@Bindable 注解干了什么?)
[1.4 可观察集合(ObservableArrayMap/ObservableArrayList)](#1.4 可观察集合(ObservableArrayMap/ObservableArrayList))
[2.1 ObservableFields](#2.1 ObservableFields)
[2.2 BaseObservable](#2.2 BaseObservable)
[2.3 updateRegistration() 方法](#2.3 updateRegistration() 方法)
[3.1 单向绑定 语法:@{}](#3.1 单向绑定 语法:@{})
[3.1.1 基础使用示例](#3.1.1 基础使用示例)
[2.1.2 源码分析](#2.1.2 源码分析)
[3.2 双向绑定 语法:@={}](#3.2 双向绑定 语法:@={})
[3.2.1 基础使用示例](#3.2.1 基础使用示例)
[3.2.2 源码分析](#3.2.2 源码分析)
[3.3 事件绑定](#3.3 事件绑定)
[3.3.1 基本使用示例](#3.3.1 基本使用示例)
[3.3.2 源码分析](#3.3.2 源码分析)
[4.1 @BindingAdapter](#4.1 @BindingAdapter)
[4.1.1 注解定义源码](#4.1.1 注解定义源码)
[4.1.2 编译时注解处理](#4.1.2 编译时注解处理)
[4.2 @BindingConversion](#4.2 @BindingConversion)
系列入口导航:Android Jetpack 概述
上一篇文章我们从源码的角度大致分析了 DataBinding 初始化流程,接下来我们聊聊【使用篇】对应功能剩下没说完的。
一、可观察数据容器
1.1 可观察字段(ObservableFields)
ObservableField 是一个可观察的对象包装器,用于在数据绑定中实现 UI 自动更新。当字段值发生变化时,会自动通知绑定的 UI 组件刷新 ,常常在 ViewModel 中使用。
使用方式如下:
java
// public final 修饰,泛型指定类型
public final ObservableField<String> name = new ObservableField<>();
public final ObservableInt age = new ObservableInt(); // 避免装箱
为什么需要使用 public final来修饰,接下来我们看看源码。
ObservableFields 源码:
java
package androidx.databinding;
import androidx.annotation.Nullable;
import java.io.Serializable;
/**
* An object wrapper to make it observable.
* <p>
* Observable field classes may be used instead of creating an Observable object. It can also
* create a calculated field, depending on other fields:
* <pre><code>public class MyDataObject {
* private Context context;
* public final ObservableField<String> first = new ObservableField<String>();
* public final ObservableField<String> last = new ObservableField<String>();
* public final ObservableField<String> display =
* new ObservableField<String>(first, last) {
* @Override
* public String get() {
* return context.getResources().getString(R.string.name, first.get, last.get());
* }
* };
* public final ObservableInt age = new ObservableInt();
* }</code></pre>
* Fields of this type should be declared final because bindings only detect changes in the
* field's value, not of the field itself.
*
* @param <T> The type parameter for the actual object.
* @see ObservableParcelable
*/
public class ObservableField<T> extends BaseObservableField implements Serializable {
static final long serialVersionUID = 1L;
private T mValue;
/**
* Wraps the given object and creates an observable object
*
* @param value The value to be wrapped as an observable.
*/
public ObservableField(T value) {
mValue = value;
}
/**
* Creates an empty observable object
*/
public ObservableField() {
}
/**
* Creates an ObservableField that depends on {@code dependencies}. Typically,
* ObservableFields are passed as dependencies. When any dependency
* notifies changes, this ObservableField also notifies a change.
*
* @param dependencies The Observables that this ObservableField depends on.
*/
public ObservableField(Observable... dependencies) {
super(dependencies);
}
/**
* @return the stored value.
*/
@Nullable
public T get() {
return mValue;
}
/**
* Set the stored value.
*
* @param value The new value
*/
public void set(T value) {
if (value != mValue) {
mValue = value;
notifyChange();
}
}
}
继承关系:
java
public class ObservableField<T> extends BaseObservableField implements Serializable
- 继承 BaseObservableField:提供依赖管理 和通知机制
- 实现 Serializable:支持序列化,可在 Intent 或 Bundle 中传递
成员变量和核心方法:
java
private T mValue; // 存储实际数据
static final long serialVersionUID = 1L; // 序列化版本ID
//get()方法
@Nullable
public T get() {
return mValue;
}
//set()方法
public void set(T value) {
if (value != mValue) { // 引用比较
mValue = value;
notifyChange(); // 触发通知
}
}
关注到 set() 其实比较的泛型的引用,如果引用没变但内部状态变了,ObservableField 不会触发通知,UI 也不会更新 。String 类型的指向的是字符串常量池,所以没有影响。但是假如是下面的情况,UI就不会自动发生变化。
java
ObservableField<User> field = new ObservableField<>();
User user = new User("张三", 20);
field.set(user); // field 持有 user 对象的引用
// 修改内部属性
user.name = "李四";
field.get().name = "李四"; // 同样效果
// set() 方法内部
// value != mValue ?
// user 引用没变,所以 value == mValue
// 不调用 notifyChange()
// UI 不更新 ❌
问题1: 为什么字段必须是 public final ?
这是 DataBinding 最容易被误解的地方。看源码注释:
bash
Fields of this type should be declared final
because bindings only detect changes in the field's value,
not of the field itself.
"此类型的字段应声明为 final,
因为绑定只检测字段值(指字段所引用的对象)的变化,
而不检测字段本身(指字段的引用)的变化。"
核心原因:DataBinding 监听的是"对象内部的变化",而不是"字段引用的变化"。
java
// ✅ 正确做法:声明为 final
public final ObservableField<String> name = new ObservableField<>("Alice");
// ❌ 错误做法:非 final
public ObservableField<String> name = new ObservableField<>("Alice");
DataBinding 在编译时会生成绑定代码(如 ActivityMainBindingImpl),这些代码会在 executeBindings() 方法中直接引用你的字段。
java
// 生成的绑定代码(简化版)
@Override
protected void executeBindings() {
// 直接读取 mViewModel.name 这个 ObservableField 对象
ObservableField<String> nameField = mViewModel.name;
// 然后注册监听器到 nameField 上
if (nameField != null) {
nameField.addOnPropertyChangedCallback(...);
}
}
如果 name 字段不是 final ,你可能在运行时把它替换成另一个 ObservableField 对象:
java
// 假设 name 不是 final
public ObservableField<String> name = new ObservableField<>("Alice");
// 后来你做了这样的操作
viewModel.name = new ObservableField<>("Bob"); // 整个对象被替换了!
这时问题就来了:
DataBinding 在初始化时监听了旧的 ObservableField 对象
你把字段指向了新的 ObservableField 对象
但 DataBinding 还持有旧对象的监听,完全不知道新对象的存在
UI 不会更新,出现诡异 bug
结论:final 确保了字段引用永不改变,DataBinding 可以安全地监听这个固定的 ObservableField 实例。
问题2:set() 方法中 notifyChange() 干了什么
研究这个问题之前,我们先跳过 BaseObservableField 来看看BaseObservable。
java
package androidx.databinding;
import androidx.annotation.NonNull;
/**
* A convenience class that implements {@link android.databinding.Observable} interface and provides
* {@link #notifyPropertyChanged(int)} and {@link #notifyChange} methods.
*/
public class BaseObservable implements Observable {
private transient PropertyChangeRegistry mCallbacks;
public BaseObservable() {
}
@Override
public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
synchronized (this) {
if (mCallbacks == null) {
mCallbacks = new PropertyChangeRegistry();
}
}
mCallbacks.add(callback);
}
@Override
public void removeOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
mCallbacks.remove(callback);
}
/**
* Notifies listeners that all properties of this instance have changed.
*/
public void notifyChange() {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
mCallbacks.notifyCallbacks(this, 0, null);
}
/**
* Notifies listeners that a specific property has changed. The getter for the property
* that changes should be marked with {@link Bindable} to generate a field in
* <code>BR</code> to be used as <code>fieldId</code>.
*
* @param fieldId The generated BR id for the Bindable field.
*/
public void notifyPropertyChanged(int fieldId) {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
mCallbacks.notifyCallbacks(this, fieldId, null);
}
}
notifyChange() 的核心语义是:通知所有监听器,这个实例的 所有 属性都已更改。
完整流程:
用个简单的例子模拟下流程。
java
public class MyViewModel {
public final ObservableField<String> firstName = new ObservableField<>("John");
public final ObservableField<String> lastName = new ObservableField<>("Doe");
public final ObservableField<String> fullName = new ObservableField<>(firstName, lastName) {
@Override
public String get() {
return firstName.get() + " " + lastName.get();
}
};
}
firstName和 lastName 此时执行的是 ObservableField(T value) 的构造方法,此时这个两个对象是没有注册监听器的,你发送notifyChange() 是不会有监听器响应的,具体在哪注册监听器,咱们后面再说。这里主要针对的是fullName ,对应的是 ObservableField(Observable... dependencies)方法。
java
public ObservableField(T value) {
mValue = value;
}
public ObservableField(Observable... dependencies) {
super(dependencies);
}
java
/**
* 可观察字段的抽象基类
* 支持依赖其他 Observable 对象,当依赖对象变化时自动触发自身通知
*/
abstract class BaseObservableField extends BaseObservable {
/**
* 无参构造器
* 创建一个独立的可观察字段,不依赖任何其他 Observable
*/
public BaseObservableField() {
}
/**
* 带依赖的构造器
* 创建一个依赖其他 Observable 对象的字段
* 当任何一个依赖对象发生变化时,当前对象也会触发变化通知
*
* @param dependencies 依赖的可观察对象数组
* 可以是 ObservableField, ObservableInt, ObservableBoolean 等
*/
public BaseObservableField(Observable... dependencies) {
// 检查依赖数组是否有效(不为 null 且长度不为 0)
if (dependencies != null && dependencies.length != 0) {
// 创建依赖回调实例
DependencyCallback callback = new DependencyCallback();
// 遍历所有依赖对象
for (int i = 0; i < dependencies.length; i++) {
// 为每个依赖对象添加属性变化监听器
// 当任何一个依赖变化时,callback.onPropertyChanged() 会被调用
dependencies[i].addOnPropertyChangedCallback(callback);
}
}
}
/**
* 依赖回调内部类
* 用于监听所有依赖对象的变化
*/
class DependencyCallback extends Observable.OnPropertyChangedCallback {
/**
* 当任何一个被监听的 Observable 属性发生变化时调用
*
* @param sender 发生变化的可观察对象
* @param propertyId 变化的属性 ID(通常为 BR.xxx 或 0 表示整个对象变化)
*/
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
// 触发当前对象的变化通知
// 这会通知所有监听当前对象的观察者:当前对象也发生了变化
// 实际效果:依赖变化 → 当前对象也被标记为"已变化"
notifyChange();
}
}
}
BaseObservableField(Observable... dependencies) 这段代码的关键逻辑是:
- 如果传入了依赖对象(dependencies 不为空)
- 创建一个 DependencyCallback实例(同一个实例,不是每个依赖都新建一个)
- 遍历所有依赖,把自己注册为每一个依赖的监听器
这意味着: 当任意一个依赖对象(比如 firstName) 发生变化时,都会回调到同一个 DependencyCallback 的 onPropertyChanged。
1.2 可观察字段(ObservableInt 等)
对比之前的 ObservableField ,可以很清晰的看到多了Parcelable和两个对应方法,是Parcelable 协议的完整实现。
java
public class ObservableInt extends BaseObservableField implements Parcelable, Serializable {
static final long serialVersionUID = 1L;
private int mValue;
public ObservableInt(int value) {
mValue = value;
}
public ObservableInt() {
}
public ObservableInt(Observable... dependencies) {
super(dependencies);
}
public int get() {
return mValue;
}
public void set(int value) {
if (value != mValue) {
mValue = value;
notifyChange();
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mValue);
}
public static final Parcelable.Creator<ObservableInt> CREATOR
= new Parcelable.Creator<ObservableInt>() {
@Override
public ObservableInt createFromParcel(Parcel source) {
return new ObservableInt(source.readInt());
}
@Override
public ObservableInt[] newArray(int size) {
return new ObservableInt[size];
}
};
}
ObservableBoolean vs ObservableField<Boolean>
java
// ObservableBoolean - 有 Parcelable 支持
ObservableBoolean bool1 = new ObservableBoolean(true);
// ✅ 可以跨进程传递
intent.putExtra("bool", bool1);
// ObservableField<Boolean> - 无 Parcelable 支持
ObservableField<Boolean> bool2 = new ObservableField<>(true);
// ❌ 不能直接通过 Intent 传递
// intent.putExtra("bool", bool2); // 编译错误
// 但可以通过 Serializable 勉强传递(性能差)
intent.putExtra("bool", (Serializable) bool2); // 不推荐
同时在之前的章节中我们就知道,ObservableBoolean 的 性能是比 ObservableField<Boolean>要高的。因为 ObservableBoolean 没有自动拆箱装箱的过程。
问题1:为什么 ObservableField<T> 不同时实现 Parcelable?
因为 ObservableField<T> 是一个泛型类,而 ObservableInt 等是针对基本类型的特化类。在 Android 中,泛型类型无法可靠地实现 Parcelable,而基本类型可以。
- Parcelable 要求类型完全确定:实现 Parcelable 必须在编译时就明确知道每个字段的具体类型,以便生成固定的写入和读取逻辑。ObservableField<T> 的泛型参数 T 可以是任意类型,编译时无法确定,因此无法实现通用的序列化方案。
- Parcel 的读写是类型相关的:Parcel 为每种基础类型提供了独立的读写方法(writeInt、writeString、writeParcelable 等)。泛型 T 无法在编译时映射到具体的读写方法,运行时判断会导致性能下降和代码复杂,且无法保证 T 类型一定支持序列化。而 ObservableInt 内部是确定的 int 类型,可以直接使用 writeInt / readInt,实现简单可靠。
其实官方提供了 ObservableParcelable<T> 来弥补 ObservableField<T> 没有实现 Parcelable。
1.3 可观察对象 (BaseObservable)
目前我们知道了下面的继承关系 ObservableFields <--- BaseObservableField <--- BaseObservable。让数据类继承BaseObservable,并在getter上添加@Bindable注解,在setter中通知变更。
java
public class User extends BaseObservable {
private String name;
private int age;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
notifyPropertyChanged(BR.age);
}
}
回看之前的代码,可以看到 BaseObservable 有两个通知方法,之前分析的源码都是通过notifyChange()来通知;如今我们可以通过 notifyPropertyChanged() 实现定向更新,数据量复杂庞大的情况下,很适合使用这种方式。
java
// BaseObservable 提供了两个方法
public class BaseObservable implements Observable {
// 粗粒度:通知所有属性都变了
public void notifyChange() {
mCallbacks.notifyCallbacks(this, 0, null); // propertyId = 0
}
// 细粒度:通知特定属性变了
public void notifyPropertyChanged(int fieldId) { // 指定具体属性ID
mCallbacks.notifyCallbacks(this, fieldId, null);
}
}
问题1:@Bindable 注解干了什么?
java
// android.databinding.Bindable
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
@Target(ElementType.METHOD) // 只能用在方法上
public @interface Bindable {
// 可选参数:指定绑定的属性名
String[] value() default {};
}
识别注解后干了什么呢?
java
// Data Binding 的注解处理器
@AutoService(Processor.class)
public class DataBindingProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
// 查找所有 @Bindable 注解
Set<? extends Element> bindableElements =
env.getElementsAnnotatedWith(Bindable.class);
for (Element element : bindableElements) {
// 提取方法名 "getName"
String methodName = element.getSimpleName().toString();
// 转换为属性名 "name"
String propertyName = getPropertyName(methodName);
// 生成 BR 字段
generateBRField(propertyName);
}
return true;
}
private String getPropertyName(String methodName) {
// getName() -> name
// isEnabled() -> enabled
if (methodName.startsWith("get")) {
return decapitalize(methodName.substring(3));
} else if (methodName.startsWith("is")) {
return decapitalize(methodName.substring(2));
}
return methodName;
}
}
说白了,就是根据你的方法名称,生成对应的 BR 字段。@Bindable 就是生成 BR.name 的关键。
1.4 可观察集合(ObservableArrayMap/ObservableArrayList)
适用于列表或Map这种结构不确定的数据 。其实跟之前 ObservableFields 的实现逻辑差不多,只不过类型换成了适配 ArrayMap 和 ArrayList
ObservableArrayMap:
java
public class ObservableArrayMap<K, V> extends ArrayMap<K, V> implements ObservableMap<K, V> {
private transient MapChangeRegistry mListeners;
@Override
public void addOnMapChangedCallback(
OnMapChangedCallback<? extends ObservableMap<K, V>, K, V> listener) {
if (mListeners == null) {
mListeners = new MapChangeRegistry();
}
mListeners.add(listener);
}
@Override
public void removeOnMapChangedCallback(
OnMapChangedCallback<? extends ObservableMap<K, V>, K, V> listener) {
if (mListeners != null) {
mListeners.remove(listener);
}
}
@Override
public void clear() {
boolean wasEmpty = isEmpty();
if (!wasEmpty) {
super.clear();
notifyChange(null);
}
}
public V put(K k, V v) {
V val = super.put(k, v);
notifyChange(k);
return v;
}
@Override
public boolean removeAll(Collection<?> collection) {
boolean removed = false;
for (Object key : collection) {
int index = indexOfKey(key);
if (index >= 0) {
removed = true;
removeAt(index);
}
}
return removed;
}
@Override
public boolean retainAll(Collection<?> collection) {
boolean removed = false;
for (int i = size() - 1; i >= 0; i--) {
Object key = keyAt(i);
if (!collection.contains(key)) {
removeAt(i);
removed = true;
}
}
return removed;
}
@Override
public V removeAt(int index) {
K key = keyAt(index);
V value = super.removeAt(index);
if (value != null) {
notifyChange(key);
}
return value;
}
@Override
public V setValueAt(int index, V value) {
K key = keyAt(index);
V oldValue = super.setValueAt(index, value);
notifyChange(key);
return oldValue;
}
private void notifyChange(Object key) {
if (mListeners != null) {
mListeners.notifyCallbacks(this, 0, key);
}
}
}
java
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("name", "张三");
user.put("age", 20);
binding.setUser(user);
ObservableArrayList:
java
public class ObservableArrayList<T> extends ArrayList<T> implements ObservableList<T> {
private transient ListChangeRegistry mListeners = new ListChangeRegistry();
@Override
public void addOnListChangedCallback(OnListChangedCallback listener) {
if (mListeners == null) {
mListeners = new ListChangeRegistry();
}
mListeners.add(listener);
}
@Override
public void removeOnListChangedCallback(OnListChangedCallback listener) {
if (mListeners != null) {
mListeners.remove(listener);
}
}
@Override
public boolean add(T object) {
super.add(object);
notifyAdd(size() - 1, 1);
return true;
}
@Override
public void add(int index, T object) {
super.add(index, object);
notifyAdd(index, 1);
}
@Override
public boolean addAll(Collection<? extends T> collection) {
int oldSize = size();
boolean added = super.addAll(collection);
if (added) {
notifyAdd(oldSize, size() - oldSize);
}
return added;
}
@Override
public boolean addAll(int index, Collection<? extends T> collection) {
boolean added = super.addAll(index, collection);
if (added) {
notifyAdd(index, collection.size());
}
return added;
}
@Override
public void clear() {
int oldSize = size();
super.clear();
if (oldSize != 0) {
notifyRemove(0, oldSize);
}
}
@Override
public T remove(int index) {
T val = super.remove(index);
notifyRemove(index, 1);
return val;
}
@Override
public boolean remove(Object object) {
int index = indexOf(object);
if (index >= 0) {
remove(index);
return true;
} else {
return false;
}
}
@Override
public T set(int index, T object) {
T val = super.set(index, object);
if (mListeners != null) {
mListeners.notifyChanged(this, index, 1);
}
return val;
}
@Override
protected void removeRange(int fromIndex, int toIndex) {
super.removeRange(fromIndex, toIndex);
notifyRemove(fromIndex, toIndex - fromIndex);
}
private void notifyAdd(int start, int count) {
if (mListeners != null) {
mListeners.notifyInserted(this, start, count);
}
}
private void notifyRemove(int start, int count) {
if (mListeners != null) {
mListeners.notifyRemoved(this, start, count);
}
}
}
java
ObservableArrayList<Object> user = new ObservableArrayList<>();
user.add("张三"); // index 0
user.add(20); // index 1
binding.setUser(user);
二、可观察数据容器监听的绑定
上一章节,我们基于 基础数据对象 (POJO) 来探究 DataBinding 的源码流程。本章讲解的观察者对象的相关内容还没有提及到,这里我们将其补全。当使用可观察数据容器时,对比之前的你会发现多了一个方法**updateRegistration() ,**这个方法就是注册的关键。
2.1 ObservableFields
java
public class User1 {
public final ObservableField<String> name = new ObservableField<>();
public final ObservableInt age = new ObservableInt(); // 避免装箱
public User1(String name, int age) {
this.name.set(name);
this.age.set(age);
}
}
当我在XML只使用了 name ,而没有使用 age :在 ActivityMainBindingImpl.java 中的executeBindings() 发现了 updateRegistration(0, user1Name);
- updateRegistration() 方法在 executeBindings() 中
- 只生成 XML 中实际使用到的字段的监听器代码。
2.2 BaseObservable
java
public class User extends BaseObservable
当我使用 BaseObservable 的时候情况就完全不一样了:
- updateRegistration() 方法在 setUser() 中
- 在XML中使用的字段没有找到对于的updateRegistration()
| 方式 | updateRegistration 位置 | 注册次数 |
|---|---|---|
| BaseObservable | setUser() 方法中 |
1次(设置时) |
| ObservableField | executeBindings() 方法中 |
可能多次(每次执行绑定时检查) |
2.3 updateRegistration() 方法
java
// 在 ViewDataBinding 基类中
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
具体代码:
java
/**
* 更新指定本地字段的观察者注册状态
*
* @param localFieldId 本地字段的唯一ID,用于标识需要观察的字段
* @param observable 新的可观察对象(如 ObservableField、LiveData 等)
* @param listenerCreator 用于创建弱监听器的工厂对象
* @return true 表示观察者状态发生变化(新增或更换了观察对象),false 表示无变化(同一对象)
*/
private boolean updateRegistration(int localFieldId, Object observable,
ViewDataBinding.CreateWeakListener listenerCreator) {
// 情况1:新传入的可观察对象为 null
// 此时需要移除该字段上原有的任何监听器
if (observable == null) {
return unregisterFrom(localFieldId);
}
// 从缓存数组中取出该字段当前的弱监听器(可能为 null)
ViewDataBinding.WeakListener listener = mLocalFieldObservers[localFieldId];
// 情况2:当前没有任何监听器(首次注册)
if (listener == null) {
registerTo(localFieldId, observable, listenerCreator); // 注册新的监听器
return true; // 表示发生了注册动作
}
// 情况3:已有监听器,检查其指向的目标对象是否就是当前传入的 observable
// 注意:WeakListener 内部持有对 observable 的弱引用,getTarget() 可能返回 null(已被GC)
if (listener.getTarget() == observable) {
return false; // 同一个对象,无需重复注册,什么也不做
}
// 情况4:已有监听器,但目标对象不同(或原来的对象已被回收,返回 null)
// 需要先移除旧的监听器,再为新对象创建并注册监听器
unregisterFrom(localFieldId); // 解绑旧的 observable
registerTo(localFieldId, observable, listenerCreator); // 绑定新的 observable
return true; // 表示发生了替换
}
| 情况 | 条件 | 行为 | 返回值 |
|---|---|---|---|
| 取消注册 | observable == null |
调用 unregisterFrom 移除监听 |
true(发生了变更) |
| 首次注册 | listener == null |
调用 registerTo 新增监听 |
true |
| 对象未变 | listener.getTarget() == observable |
什么都不做 | false(无变更) |
| 对象已变 | listener != null 且指向不同对象 |
先解绑旧的,再绑定新的 | true |
三、绑定类型
3.1 单向绑定 语法:@{}
3.1.1 基础使用示例
XML
<!-- activity_main.xml -->
<layout>
<data>
<variable name="user" type="com.example.User"/>
</data>
<TextView
android:text="@{user.name}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</layout>
2.1.2 源码分析
在之前的学习我们知道了,在DataBinding通知更新后最终会执行到executeBindings() 方法
java
// 当你调用 setUser 后,最终会触发 executeBindings
binding.setUser(user)
↓
requestRebind() // 调度刷新
↓
Choreographer 下一帧
↓
executePendingBindings()
↓
executeBindings() // ← 这里执行实际赋值
DataBinding 编译后会生成ActivityMainBindingImpl.java,核心代码:
java
public class ActivityMainBindingImpl extends ActivityMainBinding {
private TextView mboundView0; // 缓存TextView引用
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0; // 清空脏标记
}
// 1. 获取数据对象
com.example.User user = mUser;
// 2. 从user中取出name值
java.lang.String userName = null;
if ((dirtyFlags & 0x3L) != 0) { // 检查user.name是否脏
if (user != null) {
userName = user.getName(); // ← 调用getter获取值
}
}
// 3. 赋值到TextView上
if ((dirtyFlags & 0x3L) != 0) {
// 关键:通过BindingAdapter设置文本
androidx.databinding.adapters.TextViewBindingAdapter.setText(
this.mboundView0, // TextView
userName // 要设置的文本
);
}
}
}
这里可以看到,如果判断数据发生了变化就使用 **TextViewBindingAdapter.setText()**实现对文本的更新。
所以,@{user.name} 的值是在 executeBindings() 中通过 user.getName() 获取,然后通过 TextViewBindingAdapter.setText() 最终调用 textView.setText() 完成赋值的。
3.2 双向绑定 语法:@={}
3.2.1 基础使用示例
XML
<!-- activity_main.xml -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user1"
type="com.example.User1"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 双向绑定:EditText 既能显示又能修改数据 -->
<EditText
android:text="@={user1.name}"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
3.2.2 源码分析
DataBinding 的单向绑定最终会执行到 executeBindings() 方法。而双向绑定 在单向绑定的基础上,增加了 反向监听 机制。
DataBinding 编译后会生成 ActivityMainBindingImpl.java,核心代码:
java
/**
* DataBinding 编译生成的绑定实现类
* 原始布局文件包含:<EditText android:text="@={user1.name}" />
*/
public class ActivityMain2BindingImpl extends ActivityMain2Binding {
// ==================== 静态缓存区 ====================
@Nullable
private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;
@Nullable
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null; // 没有引用的布局文件
sViewsWithIds = null; // 没有需要特殊处理的 View ID
}
// ==================== View 引用 ====================
@NonNull
private final android.widget.LinearLayout mboundView0; // 根布局
@NonNull
private final android.widget.EditText mboundView1; // 双向绑定的 EditText
// ==================== 反向绑定监听器(双向绑定的核心)====================
/**
* 反向绑定监听器:当 EditText 的文本发生变化时被调用
* 作用:将 View 的变化同步回 ViewModel
*/
private androidx.databinding.InverseBindingListener mboundView1androidTextAttrChanged =
new androidx.databinding.InverseBindingListener() {
@Override
public void onChange() {
// 步骤1:从 EditText 中获取当前文本
// getTextString() 内部调用 view.getText().toString()
java.lang.String callbackArg_0 =
androidx.databinding.adapters.TextViewBindingAdapter.getTextString(mboundView1);
// 步骤2:局部变量声明(线程安全考虑)
boolean user1NameJavaLangObjectNull = false;
java.lang.String user1NameGet = null;
com.example.myjetpack.User1 user1 = mUser1; // 获取 ViewModel 对象
androidx.databinding.ObservableField<java.lang.String> user1Name = null;
boolean user1JavaLangObjectNull = false;
// 步骤3:检查 ViewModel 是否为空
user1JavaLangObjectNull = (user1) != (null);
if (user1JavaLangObjectNull) {
// 获取 ObservableField 属性
user1Name = user1.name;
user1NameJavaLangObjectNull = (user1Name) != (null);
if (user1NameJavaLangObjectNull) {
// 步骤4:核心!将 View 的值设置回 ViewModel
// 这会触发 ObservableField 的通知机制
user1Name.set(((java.lang.String) (callbackArg_0)));
}
}
}
};
// ==================== 构造函数 ====================
public ActivityMain2BindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent,
@NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 2, sIncludes, sViewsWithIds));
}
/**
* 私有构造函数,初始化 View 引用
* @param bindingComponent 绑定组件(可用于自定义适配器)
* @param root 根 View
* @param bindings 解析出的 View 数组
*/
private ActivityMain2BindingImpl(androidx.databinding.DataBindingComponent bindingComponent,
View root,
Object[] bindings) {
super(bindingComponent, root, 1);
// 初始化 View 引用(索引来自 mapBindings 的解析结果)
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.mboundView1 = (android.widget.EditText) bindings[1];
this.mboundView1.setTag(null);
setRootTag(root);
// 标记所有绑定都需要初始化
invalidateAll();
}
// ==================== 生命周期方法 ====================
/**
* 标记所有绑定为脏,强制重新绑定
* 设置标志位 0x4L,表示需要重新设置监听器
*/
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x4L; // 设置需要设置监听器的标志
}
requestRebind(); // 请求在下一帧执行绑定
}
/**
* 检查是否有待处理的绑定更新
*/
@Override
public boolean hasPendingBindings() {
synchronized(this) {
if (mDirtyFlags != 0) {
return true;
}
}
return false;
}
// ==================== 变量设置方法 ====================
/**
* 通过变量 ID 设置变量(BR 文件生成的常量)
* @param variableId 变量 ID(如 BR.user1)
* @param variable 变量值
*/
@Override
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.user1 == variableId) {
setUser1((com.example.myjetpack.User1) variable);
} else {
variableSet = false;
}
return variableSet;
}
/**
* 设置 ViewModel 对象
* 设置标志位 0x2L,表示 user1 对象发生了变化
*/
public void setUser1(@Nullable com.example.myjetpack.User1 User1) {
this.mUser1 = User1;
synchronized(this) {
mDirtyFlags |= 0x2L; // 标记 user1 对象需要重新读取
}
notifyPropertyChanged(BR.user1); // 通知属性变化
super.requestRebind(); // 请求重新绑定
}
// ==================== 属性变化监听(观察者模式)====================
/**
* 当 ObservableField 属性发生变化时被调用
* @param localFieldId 局部字段 ID(0 代表 user1.name)
* @param object 变化的对象(ObservableField)
* @param fieldId 字段 ID
*/
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0: // user1.name 属性
return onChangeUser1Name((androidx.databinding.ObservableField<java.lang.String>) object, fieldId);
}
return false;
}
/**
* user1.name 属性变化时的回调
* 设置标志位 0x1L,表示 name 属性需要重新读取
*/
private boolean onChangeUser1Name(androidx.databinding.ObservableField<java.lang.String> User1Name, int fieldId) {
if (fieldId == BR._all) { // BR._all 表示整个对象的变化
synchronized(this) {
mDirtyFlags |= 0x1L; // 标记 name 属性需要更新
}
return true;
}
return false;
}
// ==================== 执行绑定的核心方法 ====================
/**
* 执行实际的绑定操作(单向 + 双向)
* 这是 DataBinding 的核心方法,在每一帧被调用
*/
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags; // 获取当前脏标志
mDirtyFlags = 0; // 重置脏标志
}
java.lang.String user1NameGet = null;
com.example.myjetpack.User1 user1 = mUser1;
androidx.databinding.ObservableField<java.lang.String> user1Name = null;
// ========== 阶段1:读取数据模型 ==========
// 0x7L = 111 (二进制),包含标志位 0x1L、0x2L、0x4L
if ((dirtyFlags & 0x7L) != 0) {
// 从 ViewModel 读取 user1 对象
if (user1 != null) {
user1Name = user1.name; // 获取 ObservableField
}
// 注册观察者:监听 user1Name 的变化
// localFieldId = 0 对应 case 0: onChangeUser1Name()
updateRegistration(0, user1Name);
// 读取实际的值
if (user1Name != null) {
user1NameGet = user1Name.get(); // 调用 ObservableField.get()
}
}
// ========== 阶段2:更新 UI(单向绑定方向)==========
// 将数据模型的值设置到 View 上
if ((dirtyFlags & 0x7L) != 0) {
// 调用适配器设置文本(ViewModel → View)
androidx.databinding.adapters.TextViewBindingAdapter.setText(
this.mboundView1,
user1NameGet
);
}
// ========== 阶段3:设置反向监听器(双向绑定方向)==========
// 只在初始化时执行一次,后续更新不会重复设置
if ((dirtyFlags & 0x4L) != 0) {
// 为 EditText 设置 TextWatcher,用于监听文本变化
// 当文本变化时,会回调 mboundView1androidTextAttrChanged.onChange()
androidx.databinding.adapters.TextViewBindingAdapter.setTextWatcher(
this.mboundView1,
null, // BeforeTextChanged 回调
null, // OnTextChanged 回调
null, // AfterTextChanged 回调
mboundView1androidTextAttrChanged // ← 反向绑定监听器(核心)
);
}
}
// ==================== 脏标志位管理 ====================
/**
* 脏标志位(64位整数)
* 0xffffffffffffffffL = 所有位都是1,表示初始状态所有标志位都脏
*
* 标志位映射:
* flag 0 (0x1L): user1.name 属性变化
* flag 1 (0x2L): user1 对象变化
* flag 2 (0x4L): 需要设置监听器(特殊标志,只在初始化时使用)
*/
private long mDirtyFlags = 0xffffffffffffffffL;
/* flag mapping
flag 0 (0x1L): user1.name
flag 1 (0x2L): user1
flag 2 (0x3L): null
flag mapping end*/
}
流程如下
bash
// ==================== 注释说明 ====================
/**
* 完整工作流程:
*
* 1. 初始化阶段:
* - 构造函数调用 invalidateAll(),设置 mDirtyFlags = 0x4L
* - requestRebind() 触发 executeBindings()
* - executeBindings() 中检测到 0x4L 标志,调用 setTextWatcher() 设置监听器
*
* 2. 用户输入阶段(View → ViewModel):
* - 用户修改 EditText 文本
* - TextWatcher 回调 → mboundView1androidTextAttrChanged.onChange()
* - 调用 user1.name.set(newValue) 更新 ViewModel
* - ObservableField.set() 触发 onChangeUser1Name()
* - 设置 mDirtyFlags |= 0x1L,调用 requestRebind()
*
* 3. 刷新 UI 阶段(ViewModel → View):
* - executeBindings() 再次执行
* - 检测到 0x1L 标志,读取新的 user1NameGet
* - 调用 setText() 更新 EditText
* - 注意:此时不会再次触发 TextWatcher(防循环机制)
*
* 4. 外部更新阶段(代码修改 ViewModel):
* - 调用 setUser1() 或 user1.name.set()
* - 设置对应的脏标志(0x2L 或 0x1L)
* - 触发 executeBindings() 刷新 UI
* - 不会重新设置 TextWatcher(因为没有 0x4L 标志)
*/
**setTextWatcher 会在初始化调用一次,**不会随着set 的触发反复调用。
java
// 1. 用户在 EditText 输入 "A"
EditText.afterTextChanged("A")
↓
// 2. 反向绑定:更新 Model
user.setName("A")
↓
// 3. Model 变化触发通知
notifyPropertyChanged(BR.name)
↓
// 4. 单向绑定:更新 UI
textView.setText("A")
↓
// 5. setText 又会触发 TextWatcher
EditText.afterTextChanged("A") // 再次触发,形成循环
↓
// 无限循环...
到底会不会无限循环下去,看看 TextViewBindingAdapter.setText。里面会跟原生数据比较,并不会导致循环。
java
@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
final CharSequence oldText = view.getText();
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
if (text instanceof Spanned) {
if (text.equals(oldText)) {
return; // No change in the spans, so don't set anything.
}
} else if (!haveContentsChanged(text, oldText)) {
return; // No content changes, so don't set anything.
}
view.setText(text);
}
3.3 事件绑定
3.3.1 基本使用示例
java
public class Handler {
// 方法签名必须匹配 View.OnClickListener
public void onButtonClick(View view) {
// 处理点击事件
Log.d("TAG", "Button clicked");
}
// 也可以带多个参数
public void onButtonClick(View view, int id) {
// 但 DataBinding 会自动匹配
}
}
XML
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="handler"
type="com.example.Handler" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me"
android:onClick="@{handler::onButtonClick}" />
</LinearLayout>
</layout>
3.3.2 源码分析
java
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0; // 清空脏标记
}
// 1. 获取当前 handler
com.example.myjetpack.Handler handler = mHandler;
// 2. 声明监听器变量
android.view.View.OnClickListener handlerOnButtonClickAndroidViewViewOnClickListener = null;
// 3. 检查脏标记,决定是否重新创建监听器
if ((dirtyFlags & 0x3L) != 0) {
if (handler != null) {
// 4. 创建或复用监听器,并设置 handler
handlerOnButtonClickAndroidViewViewOnClickListener =
(((mHandlerOnButtonClickAndroidViewViewOnClickListener == null)
? (mHandlerOnButtonClickAndroidViewViewOnClickListener = new OnClickListenerImpl())
: mHandlerOnButtonClickAndroidViewViewOnClickListener)
.setValue(handler));
}
}
// 5. 设置监听器到 Button
if ((dirtyFlags & 0x3L) != 0) {
this.button.setOnClickListener(handlerOnButtonClickAndroidViewViewOnClickListener);
}
}
编译时将方法引用转换为 View.OnClickListener,通过单例监听器复用机制绑定到 Button。
四、注解
4.1 @BindingAdapter
具体使用这里就不多说了,之前的章节有。包括上面说到的 TextViewBindingAdapter.setText() 等,都是使用 @BindingAdapter 注解的方式实现的。
4.1.1 注解定义源码
java
package android.databinding;
@Target(Method)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindingAdapter {
/**
* 属性名称数组,支持同时绑定多个属性
*/
String[] value();
/**
* 是否需要在属性变化时重新调用
* 默认 true,表示当绑定的属性值发生变化时会触发方法调用
*/
boolean requireAll() default true;
}
4.1.2 编译时注解处理
java
// DataBindingProcessor 核心处理逻辑(简化版)
public class DataBindingProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// 收集所有@BindingAdapter注解的方法
Set<? extends Element> elements =
roundEnv.getElementsAnnotatedWith(BindingAdapter.class);
for (Element element : elements) {
ExecutableElement method = (ExecutableElement) element;
BindingAdapter annotation =
method.getAnnotation(BindingAdapter.class);
// 解析注解参数
String[] attributes = annotation.value();
boolean requireAll = annotation.requireAll();
// 生成适配器代码
generateBindingAdapter(method, attributes, requireAll);
}
return true;
}
private void generateBindingAdapter(ExecutableElement method,
String[] attributes,
boolean requireAll) {
// 生成类似以下代码:
// android.databinding.adapters.TextViewBindingAdapter
}
}
我们看下 @BindingAdapter(value = {"fadeIn", "duration"}, requireAll = false) :行代码定义了自定义 XML 属性与 Java/Kotlin 方法之间的映射关系。
简单来说,它告诉 Android 系统:"当你在 XML 布局文件中给某个 View 设置了 app:fadeIn 或 app:duration 属性时,请调用这个 Java 方法来处理。"
XML
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fadeIn="@{true}"
app:duration="@{500}" />
| XML 属性 | Java 方法参数 | 说明 |
|---|---|---|
app:fadeIn |
boolean shouldFade |
对应第一个属性 |
app:duration |
int duration |
对应第二个属性 |
| (自动注入) | View view |
第一个参数永远是该属性绑定的 View 本身 |
| 特性 | requireAll = true (默认) | requireAll = false |
|---|---|---|
| 触发条件 | 必须同时在 XML 中声明所有定义的属性 | 只要 XML 中声明了其中任意一个属性即可触发 |
| 缺失处理 | 如果缺少任何一个属性,编译直接报错 | 缺失的属性将传入对应类型的默认值 |
| 典型用途 | 属性之间强关联(缺一不可,如 layout_width 和 layout_height) |
属性可选,或者有默认行为(如你的例子: duration 可选) |
Data Binding 并不关心你的静态方法叫什么(例如叫 setFadeIn、applyAnimation 甚至 abc 都可以)。它唯一识别的是注解 @BindingAdapter("...") 括号里的字符串。
4.2 @BindingConversion
java
public class ColorConversions {
// 场景1:颜色资源ID -> ColorDrawable
@BindingConversion
public static ColorDrawable convertColorToDrawable(int colorResId) {
return new ColorDrawable(colorResId);
}
// 场景2:颜色字符串 -> ColorDrawable
@BindingConversion
public static ColorDrawable convertColorStringToDrawable(String colorHex) {
if (colorHex == null) return null;
try {
int color = Color.parseColor(colorHex);
return new ColorDrawable(color);
} catch (IllegalArgumentException e) {
return null;
}
}
// 场景3:整型颜色值 -> ColorStateList
@BindingConversion
public static ColorStateList convertColorToColorStateList(int color) {
return ColorStateList.valueOf(color);
}
}
习惯建议 :通常采用convert[TypeA]To[TypeB] 的形式。
-
比如:convertStringToColor 或 dateToText。
-
因为它是静态全局的,清晰的命名有助于你在代码搜索时快速定位。
其实他也不关心命名是什么,只根据传参 TypeA和 返回类型 TypeB决定,起名字只是为了方便查找。
| 维度 | @BindingAdapter | @BindingConversion |
|---|---|---|
| 匹配依据 | 注解里的 字符串名称 | 方法的 参数和返回值类型 |
| XML 关联 | 必须在 XML 中写出对应的属性名 | 隐式触发,XML 中看不见转换器名字 |
| 冲突判定 | 属性名冲突时报错 | 输入/输出类型组合重复时报错 |
| 作用域 | 针对特定 View 的特定属性 | 全局生效,只要类型匹配就尝试转换 |