12/08/2018, 13:55

Android Data Binding: Observability(Giữ UI Luôn hiển thị dữ liệu mới nhất)

Android Data Binding rất dễ sử dụng để chèn dữ liệu vào UI(Giao diện người dùng). Tuy nhiên, khi dữ liệu được cập nhật mới thì không có một thông báo gì đến UI để UI có thể cập nhật lại. Thực sự thì nó không được tốt lắm khi servergửi một thông báo cập nhật và bạn muốn người dùng sẽ nhìn thấy sự ...

Android Data Binding rất dễ sử dụng để chèn dữ liệu vào UI(Giao diện người dùng). Tuy nhiên, khi dữ liệu được cập nhật mới thì không có một thông báo gì đến UI để UI có thể cập nhật lại. Thực sự thì nó không được tốt lắm khi servergửi một thông báo cập nhật và bạn muốn người dùng sẽ nhìn thấy sự thay đổi đó trên UI. Android Data Binding cung cấp một vài cách giúp bạn có thể đồng bộ dữ liệu với UI. Cho nên ta có thuật ngữ Observability và hôm nay tôi giới thiệu đến các bạn Interface Observable.

1.Model

Tôi khởi tạo một POJO có tên là Foo, với một thuộc tính là bar. Sau đó tôi sẽ bind vào UI của tôi.

public class Foo {
    private int bar;

    public int getBar() {
        return bar;
    }

    public void setBar(int bar) {
        this.bar = bar;
    }
}

Cái tôi mong muốn ở đây là UI của tôi sẽ thay đổi mỗi khi tôi gọi method setBar. Vì vậy hãy xem nào thế nào để Data Binding theo kịp với sự thay đổi của dữ liệu nhé.

2.Observable

Đối tượng của bạn implement lại interface Observable, và override lại 2 methods:

 void addOnPropertyChangedCallback(OnPropertyChangedCallback c);

void removeOnPropertyChangedCallback(OnPropertyChangedCallback c);

Đối tượng OnPropertyChangedCallback sẽ tạo ra các callback để thông báo mỗi khi các field trong object của bạn có sự thay đổi.

 public abstract void onPropertyChanged(Observable sender,
                                       int propertyId);

Class của bạn phải nên sử dụng annotate @Bindable với mỗi getter.

 @Bindable
public int getBar() {
    return bar;
}

Đoạn code ở trên nói rằng DataBinding Framework có thuộc tính là observable và cũng tự động generate ra một định danh trong class BR trong ứng dụng của bạn. Class BR này giống với class R, Nhưng nó ràng buộc các nguồn tài nguyên mà bạn sẽ sử dụng cho propertyId.

Android data binding cung cấp cho bạn class PropertyChangeRegistry để làm dễ dàng hơn cho việc track listener :

public class Foo implements Observable {
    private PropertyChangeRegistry registry =
        new PropertyChangeRegistry();
    private int bar;

    @Binder
    public int getBar() {
        return bar;
    }

    public void setBar(int bar) {
        this.bar = bar;
        registry.notifyChange(this, BR.bar);
    }

    @Override
    public void addOnPropertyChangedCallback(
                   OnPropertyChangedCallback callback) {
        registry.add(callback);
    }

    @Override
    public void removeOnPropertyChangedCallback(
                   OnPropertyChangedCallback callback) {
        registry.remove(callback);
    }
}

3. BaseObservable

Nếu Model của bạn kế thừa đối tượng BaseObservable. Vì việc làm Observability sẽ dễ dàng hơn bằng cách thực thi các function nằm trong Observable interface. Bạn chỉ cần phải annotate thuộc tính và notify sự thay đổi

public class Foo extends BaseObservable {
    private int bar;

    @Bindable
    public int getBar() {
        return bar;
    }

    public void setBar(int bar) {
        this.bar = bar;
        notifyPropertyChanged(BR.bar);
    }
}

4.ObservableField

Observable và BaseObservable sử dụng đơn giản nhưng cũng có chút gì đó phức tạp. Vì vậy tôi giới thiệu thêm ObservableField và các kiểu dữ liệu nguyên thủy. Để sử dụng ObservableField, chỉ có thể khai báo public final fields đó trong class của bạn. Tôi sẽ có 2 thuộc tính tại thời điểm này một là dữ liệu kiểu nguyên thủy và kiểu reference :

 public class Foo {
    public final ObservableInt bar = new ObservableInt();
    public final ObservableField<String> baz =
        new ObservableField<>();

Bạn có thể sử dụng nó giống như các thuộc tính bình thường khi dùng biểu thức binding:

 <TextView
    android:layout_awidth="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{foo.baz}"/>

Trong khi các biểu thức binding vẫn còn được sử dụng, Java yêu cầu gọi setters và getters:

 foo.bar.set(age);
String name = foo.baz.get();

5.ObservableMap

Có khi bạn sử dụng dữ liệu không thể xác định được. Bạn vẫn có thể tạo ra các prototype và các data format từ server trả về và bạn không muốn tạo ra các đối tượng cho họ thì đã có ObservableMap và ObservableArrayMap giúp bạn tạo ra các cấu trúc dữ liệu mà bạn có thể lờ mờ mường tượng ra. Với ObservableMap, bạn có thể truy cập các giá trị trong Map<> của bạn giống như bất kỳ HashMap, LinkedHashMap hay Map và UI sẽ được cập nhật khi có thay đổi xảy ra. Ví dụ :

<data>
    <variable name="product"
    type="android.databinding.ObservableMap<String, Object>"/>
</data>

 
<TextView
    android:layout_awidth="wrap_content"
    android:layout_height="wrap_content"
    android:text='@{product["name"]}'/>

Và bạn sử dụng ObservableMap trên code của bạn như sau :

ObservableMap<String, Object> product = new ObservableArrayMap<>();
binding.setProduct(product);
product.put("name", "Golf Ball");

Và bất cứ lúc nào đối tượng product thay đổi, nó sẽ cập nhật lại UI.

6. Kết luận

Nhiều UI không cần thiết phải nhận được thông tin khi có thay đổi dữ liệu, vì vậy bạn không cần phải thêm observability vào trong toàn bộ các model của bạn .Mặt khác nó có thể sẽ rất tốt để giao diện của bạn sẽ tự động cập nhật khi server của bạn gửi dữ liệu mới xuống hoặc người dùng làm thay đổi một phần của UI và các phần UI còn lại sẽ cập nhật theo phần thay đổi đó. Xong rồi. giờ hãy chọn một trong các observability ở phía trên mà bạn cảm thấy phù hợp. Hầu hết theo tôi thấy thì BaseObservable hoặc ObservableFields được sử dụng nhiều. Nhưng các observability cũng đều có các công dụng riêng của chúng.

0