27.5 C
Bangalore
December 8, 2018
Untitled

MVVM Pattern using Databinding and RxJava – Part 2

In Part 1 we saw how to setup a MVVM pattern using RxJava and Databinding. In this lesson we will see how to unsubscribe from Observable subscription whenever the view is destroyed. Also we will see how to handle button clicks in ViewModel without referencing to the view.

Code is available at: https://github.com/jacksvarghese86/RxJavaIntro

Unsubscribing from Observable automatically

In previous lesson we manually unsubscribed from the Observable when view is destroyed. Forgot to do that will result in memory leaks, also will need to write more boiler plate code. We are trying to find an alternative approach that can automatically remove subscriptions when view is no longer available. Lets see the steps in layman’s terms.
a. Connect Observable to an ObservableField.
b. Bind this ObservableField to View. Lifecycle of ObservableField will follow lifecycle of View.
b. Subscribe to Observable when ObservableField is initiated.
c. UnSubscribe from Observable when ObservableField is destroyed.

Connecting Observable to an ObservableField

Create a class called ReadOnlyField which extends from ObservableField, and connect to Observable in the constructor.

private ReadOnlyField(@NonNull Observable<T> source){
    super();
    /**
     * Readonly field is updated from the latest value of observable.
     */
    this.source = source.doOnNext(new Consumer<T>() {
            @Override
            public void accept(T t) throws Exception {
                ReadOnlyField.super.set(t);
            }
        }).share();
}

Subscribe to Observable

Subscribe to Observable only when the ReadOnlyField is first initiated.

@Override
public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
    super.addOnPropertyChangedCallback(callback);
    //subscribing to the observable
    //Subscription is mapped to the callback object
    subscriptions.put(callback, source.subscribe());
}

UnSubscribe from Observable

ViewDataBinding removes all listeners before getting deallocated.
Hence all subscriptions are removed in next GC after view is destroyed.

@Override
public void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
    super.removeOnPropertyChangedCallback(callback);
    //unsubscribing from the observable
    Disposable subscription = subscriptions.remove(callback);
    if (subscription != null && !subscription.isDisposed()) {
        subscription.dispose();
    }
}

Final step is to create our ReadOnlyField<Boolean>. The resulting Observable from combineLatest operator is converted to ReadOnlyField and is bound to the button.

Handle Button clicks

Second portion of this lesson is about handling clicks in a MVVM pattern. Click callbacks in android widgets are delivered through View.OnClickListener. But we cannot use View.OnClickListener in our ViewModel since it has reference to View.
So we are going to use a technique called ‘BindingConversion’ available in data binding library.
Using this conversion we can convert our callback interface to View.OnClickListener.
For callback interface I am going to use io.reactivex.functions.Action class.

Create a class called BindingUtils and add this utility method to it. We will use this class in future for adding all sorts of converters and BindingAdapters.

@BindingConversion
public static View.OnClickListener toOnClickListener(final Action listener) {
    if (listener != null) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    listener.run();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }};
    } else {
          return null;
    }
}

Now the converter is ready. We have to implement Action and tie that to our sign in button.

public Action signIn;

signIn = new Action() {
            @Override
            public void run() throws Exception {
                Log.d(TAG, "signIn button clicked");
            }
         };

Bind the signIn Action to onClick property of button.

<Button
android:id="@+id/signInButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sign In"
android:onClick="@{viewModel.signIn}"
android:enabled="@{viewModel.enableLogin}" />

Next Steps

In the next session we will see how to use RecyclerView in a MVVM setup. Stay tuned for that.

Code is available at: https://github.com/jacksvarghese86/RxJavaIntro

Related posts

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.