Android 从观察者模式到DataBinding (上篇)

达芬奇密码2018-06-20 11:58

前言

做过 iOS 的同学应该都了解过 KVO,是观察者模式在 Objective-C 中的应用。使用 KVO,能很方便的实现对对象属性的监听,参见探索KVC/KVO的实现原理-KVC 一探索KVC/KVO的实现原理-KVO 二

虽然 iOS 提供了对对象属性的观察者模式机制,但想想很多 Android 同学们应该不会在意。这不是很容易么,我分分钟也能写一个:

public class User {
    String mName;
    Observable mObservable;

    public User(String name) {
        mName = name;
    }

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        boolean isSame = TextUtils.equals(this.mName, name);
        this.mName = name;
        if (!isSame && mObservable != null) {
            mObservable.onNameChanged(name);
        }
    }

    public void setObservable(Observable observer) {
        this.mObservable = observer;
    }

    public interface Observable {
        void onNameChanged(String newName);
    }
}
User user = new User("我叫王尼玛");
user.setObservable(new User.Observable() {
    @Override
    public void onNameChanged(String newName) {
        Log.i("user newName = ", newName);
    }
});
user.setName("呵呵,这你都信");

但是冷静下来想想,如果一个大的工程中有很多这种需求呢?是不是 User1User2,...... 都要写这些机械的代码了?那回过头想想,如果不想自己写这些代码的话,那么我们大 Android 真的就没有这种机制么?想想不服气,于是翻了翻资料,果然我们还是有的: ObservableField

ObservableField

1. 使用方式

使用还是很简单的,我们直接看代码吧

ObservableField<String> name = new ObservableField<>();
name.addOnPropertyChangedCallback(
    new android.databinding.Observable.OnPropertyChangedCallback() {
        @Override
        public void onPropertyChanged(android.databinding.Observable observable, int i) {
            Log.d("name = ", "name = " + observable.toString() + "; i= " + i);
        }
    });
name.set("我叫张三");

这下舒服多了,不用自己实现 Observable 接口和 setObservable 方法,同时对于其他类型的变量,如 int,float 或者 自定义的类型,也不用重新实现了,直接定义 ObservableField<Integer>ObservableField<Float> 就行了,好开心^_^

2. 原理实现

还是直接看源码吧,反正代码量也不多 O(∩_∩)O~

public class ObservableField<T> extends BaseObservable implements Serializable {
    static final long serialVersionUID = 1L;
    private T mValue;
    ......
    public T get() {
        return mValue;
    }

    public void set(T value) {
        if (value != mValue) {
            mValue = value;
            notifyChange();
        }
    }
}
public class BaseObservable implements Observable {
    private transient PropertyChangeRegistry mCallbacks;
    ......
    @Override
    public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
        ......
        mCallbacks.add(callback);
    }

    @Override
    public synchronized void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
        if (mCallbacks != null) {
            mCallbacks.remove(callback);
        }
    }

    public synchronized void notifyChange() {
        if (mCallbacks != null) {
            mCallbacks.notifyCallbacks(this, 0, null);
        }
    }
    ......
}

注:mCallbacks.notifyCallbacks(this, 0, null); 方法中,0 表示的是 fieldID,在 dataBinding 中表示数据资源 id。因此这里并没有关联视图资源,所以这里设置为 0

可以看到,ObservableField 是一个泛型,所以支持多种类型的观察者模式。BaseObservable 是其父类,实现了观察者模式的核心代码,可以看到 addOnPropertyChangedCallbackaddOnPropertyChangedCallback 2个添加和移除监听的方法,真正的监听者都被保存到 PropertyChangeRegistry.mCallbacks (类型是 List) 里面。

当调用 ObservableFieldset 方法时,会执行 ObservableField.notifiyCallbacks 方法,如下:

public class BaseObservable implements Observable {
    public synchronized void notifyChange() {
        if (mCallbacks != null) {
            mCallbacks.notifyCallbacks(this, 0, null);
        }
    }

    ......
}

最终会执行到 callback.onPropertyChanged(sender, arg);,如下代码所示:

public class CallbackRegistry<C, T, A> implements Cloneable {
    private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
            final int endIndex, final long bits) {
           ......
        for (int i = startIndex; i < endIndex; i++) {
            ......
            mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
            .....
        }
    }

    ......
}
private static final CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback,
        Observable, Void> NOTIFIER_CALLBACK = 
        new CallbackRegistry.NotifierCallback<Observable.OnPropertyChangedCallback, Observable, Void>() {
        @Override
        public void onNotifyCallback(Observable.OnPropertyChangedCallback callback, Observable sender,
                int arg, Void notUsed) {
            callback.onPropertyChanged(sender, arg);
        }
    };

这里,我们可以看到,通过遍历的方式,去执行 callback 方法,将前面通过 BaseObservable.addOnPropertyChangedCallback 添加的全部观察者都响应了一边

3. 小结

到这里为止,虽然把 ObservableField 的观察者模式给讲清楚了,但还是感觉有些失望,内容很少很简单。不过还没完呢,Google 大神们就是基于此,玩出了 DataBinding

DataBinding

使用步骤

  1. IDE 配置

    • Android SDK API 版本 7 以上

    • 使用 Gradle 1.5.0-alpha1 及以上

    • 使用 Android Studio 1.3 及以上

  2. 配置开启 dataBinding

    在主工程的 build.gradle 中,添加代码:

     android {
         ......
    
         dataBinding {
             enabled = true
         }
     }
    
  3. 定义数据层 Model

    定义的 ObservableUser 类,继承自 BaseObservable (同前面的 ObservableField)。在 set 接口里添加 notifyPropertyChanged 调用,通知视图更新

     public class ObservableUser extends BaseObservable {
         private String firstName;
         private String lastName;
    
         public ObservableUser(String firstName, String lastName) {
             this.firstName = firstName;
             this.lastName = lastName;
         }
    
         @Bindable
         public String getFirstName() {
             return firstName;
         }
    
         @Bindable
         public String getLastName() {
             return lastName;
         }
    
         public void setFirstName(String firstName) {
             this.firstName = firstName;
             notifyPropertyChanged(com.netease.mvvmsample.BR.firstName);
         }
    
         public void setLastName(String lastName) {
             this.lastName = lastName;
             notifyPropertyChanged(com.netease.mvvmsample.BR.lastName);
         }
     }
    
  4. 定义事件响应 Handler

     public class Handler {
         private ObservableUser mObservableUser;
    
         public Handler(ObservableUser user) {
             mObservableUser = user;
         }
    
         public void onClickButton(View view) {
             mObservableUser.setLastName("呵呵呵,我变了 " + mCount++ + " 次");
         }
     }
    
  5. 定义布局代码

    在 data 标签下面,定义 model 数据和事件响应 handler。使用 @{} 分别将 Button 的文本信息和 user.lastNameButton 的点击响应和 handler.onClickButton 绑定在一起

     <?xml version="1.0" encoding="utf-8"?>
     <layout
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:tools="http://schemas.android.com/tools">
    
         <data>
             <variable
                 name="user"
                 type="com.netease.mvvmsample.ObservableUser">
             </variable>
             <variable
                 name="handler"
                 type="com.netease.mvvmsample.Handler">
             </variable>
         </data>
         <LinearLayout
             <Button
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:text="@{user.lastName}"
                 android:onClick="@{handler.onClickButton}"/>
         </LinearLayout    
     </layout>
    
  6. 设置布局和绑定数据和事件

    在定义了上面的布局 xml 文件之后,Android Studio 会自动生成 ViewModel 类。假设文件名是 activity_main.xml,那么程序编译后,会生成 ActivityMainBinding 类。代替原来的 setContentView(R.layout.activity_main); 方法,使用 ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); 来设置布局资源,并返回 binding 对象。新建数据和事件处理对象,并设置给 binding 对象

     public class MainActivity extends AppCompatActivity {
    
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
    
             ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
             ObservableUser user = new ObservableUser("Zhang", "San");
             Handler handler = new Handler(user);
             binding.setUser(user);
             binding.setHandler(handler);
         }
     }
    
  7. 效果展示

                                                                                                                                    注:这里仅仅讲了 DataBinding 的基本使用,并没有打算深入讲述 DataBinding 的进一步使用,如果有同学想要了解高级使用的话,如类方法,类型别名等,可以查看官方文档                                                                                                                                                                                                                                                                                                                                             本文来自网易实践者社区,经作者张云龙授权发布。