Sunday, October 16, 2016

Two way data binding without RxJava

A couple of weeks ago, this article by Radosław Piekarz got some tracktion on /r/androiddev. While I am a fan of RxJava myself we use it extensively in our apps at VG.no, I feel this example was not the best use case for it. As others point out in the comment field, this can easily be solved without RxJava. Here is how:


public MainViewModel() {
    OnPropertyChangedCallback buttonUpdater = new OnPropertyChangedCallback() {
        @Override
        public void onPropertyChanged(Observable sender, int propertyId) {
            updateButtonEnabledState();
        };
    };
    firstName.addOnPropertyChangedCallback(buttonUpdater);
    lastName.addOnPropertyChangedCallback(buttonUpdater);
}

private void updateButtonEnabledState() {
    boolean enable = StringUtils.isNotNullOrEmpty(firstName.get()) && StringUtils.isNotNullOrEmpty(lastName.get());
    helloButtonEnabled.set(enable);
}

What I’ve done is recoded the constructor of the viewmodel for MainActivity to hook onto the OnPropertyChanged callback of the ObservableFields for the two TextViews directly, instead of going through the hoop of RxUtils.toObservable.

You can furthermore clean up the code by writing an adapter for the OnPropertyChanged callback, which as it is is not very lambda-friendly. Introducing the helper class BindingCallbackAdapter:


public class BindingCallbackAdapter extends Observable.OnPropertyChangedCallback {
    private Callback mCallback;

    public BindingCallbackAdapter(BindingCallbackAdapter.Callback callback) {
        if (callback == null) throw new IllegalArgumentException("Callback cannot be null");
        mCallback = callback;
    }

    @Override
    public void onPropertyChanged(Observable sender, int propertyId) {
        mCallback.onChanged();
    }

    public interface Callback {
        void onChanged();
    }
}

..and the more readable MainActivity-constructor:


public MainViewModel() {
    OnPropertyChangedCallback buttonUpdater = new BindingCallbackAdapter(this::updateButtonEnabledState);
    firstName.addOnPropertyChangedCallback(buttonUpdater);
    lastName.addOnPropertyChangedCallback(buttonUpdater);
}

See full fork at github.

While I’m sure the author of the original article is aware of the possibility of this implementation, I’m afraid that too many blog posts about “RxJava for everything” spreads antipatterns. I do understand that there are cases where this could be a good pattern, for example if you need to compose the onPropertyChanged events in more complex Rx-flows, but this concrete use case is not one.

No comments:

Post a Comment