Understanding and practice of RxJava & RxBinding of Android part -2

Sharing is caring!

In my last tutorial, I have explained the basic functions and features of RxJava2. I would be recommended to check part -1 post before continue reading this post because it will help you to better understand this post. Here is my last post of  Basic understanding and practice of RxJava2 function in Android part -1.

We will continue and learn few other awesome features and functions of RxJava2 that we can use in our android development programming. As we have seen from the part-1 the basic requirement will be same here also. What you need to do add the RxJava and RxAndroid dependency in your gradle file to get the Reactive feature. For using lambda feature you have to enable the jack compiler and java_1.8 compatibility. Here are the details.

compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.0.8'
android {
    compileSdkVersion 26
    buildToolsVersion "26.0.0"
    defaultConfig {
      ......................
        jackOptions {
            enabled true
        }
    }
    .....................
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

In Rx world, one new Reactive feature incorporated that is called RxBinding. So the question is What is RxBinding?

To get the RxBinding feature, you need to add the RxBinding dependency in your gradle file.

compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'

RxBinding: RxBinding is a set of libraries that allow you to react to user interface events. Let’s take a look at examples. This is how Android developers typically react to a button click event: via the RxJava paradigm.

  Button b = (Button)findViewById(R.id.button1);
        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // do some work here
            }
        });

Now in a Reactive world, it has like that.

  Button b = (Button) findViewById(R.id.button1);
  Disposable buttonSub = RxView.clicks(b)
        .subscribe(new Consumer<Object>() {
                    @Override
                    public void accept(@NonNull Object o) throws Exception {
                        // do some work here
                    }
                });
        // make sure to unsubscribe the subscription
        // buttonSub.dispose();

Let’s take a look at another example, this time with a text change listener for an EditText:

final EditText name = (EditText) v.findViewById(R.id.name);
name.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // do some work here with the updated text
    }

    @Override
    public void afterTextChanged(Editable s) {

    }
});

Now in a Reactive world, it has like that.

  final EditText name = (EditText).findViewById(R.id.name);
        Disposable editTextSub =
                RxTextView.textChanges(name)
                        .subscribe(new Consumer<CharSequence>() {
                            @Override
                            public void accept(@NonNull CharSequence charSequence) throws Exception {
                                //// do some work with the updated text
                            }
                        });
// Make sure to unsubscribe the subscription

RxBinding will give the best Rx Architecture and makes your code structure quite good. I believe that RxBinding will provide the most efficient way of mapping listeners to Observables. It is working quite good with any design pattern.

OK. Let’s take one example which is uploaded RxBinding video to check that how RxBinding is working with RxJava? In this example, I want to search the string from the recyclerview list while entering the text on edit text. Once the user has entered the text on edit text field then in the old fashion we used text watcher that watching the user action. TexWatcher triggered action afterTextChnage to update the user interface.

In RxWorld it should be instant search. So let’s modify this feature by using the RxBinding and RxJava. We need to convert the event to the string by using map() operation and will perform the search operation on computation schedule.

searchSubscription = RxTextView.afterTextChangeEvents(searchInput)
                // Convert the event to a String
                .map(textChangeEvent -> textChangeEvent.editable().toString())
                // Perform search on computation scheduler
                .observeOn(Schedulers.computation())

But here the problem is that RxJava emitter is emitting the data very fast but the consumer is not consuming those data as fast as the emitter. For that case, we need to use time frame to handle the multiple events and take the last emitting event data and will drop other events. Now here we have mapped data which we need to switch to List of Observable Type. For that, we need to use another RxJava function called SwichMap().

Now I have Observable Data Stream Which I need to subscribe to the adapter to notify RecyclerView list update. Here we can use Android Scheduler to switched to the main thread to update the user interface.

   // If we get multiple events within 200ms, just emit the last one
    .debounce(200, TimeUnit.MILLISECONDS)
    // "Convert" the query string to a search result
    .switchMap(this::searchNames)
    // Switch back to the main thread
    .observeOn(AndroidSchedulers.mainThread())
     // Set the result on our adapter
    .subscribe(adapter::setSearchResult);

One thing always remembered, Once your job is done or task is completed then please unsubscribe the subscription to avoid the memory leak. Memory leak is very bad for the application. Here you can get the detail to why we need to take care memory leak?

@Override
    protected void onDestroy() {
        super.onDestroy();
        if (!searchSubscription.isDisposed()) {
            searchSubscription.dispose();
        }
    }

Here is the result for an instant search of RxJava and RxBinding. Here I am doing a query on minimum length 2.

Here is the complete source code of this example.

public class InstantSearchActivity extends AppCompatActivity {
    private static final String TAG = "InstantSearchActivity";
    private static final int MIN_LENGTH = 2;
    private EditText searchInput;
    private RecyclerView searchResult;
    private Disposable searchSubscription;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.instant_search);
        searchInput = (EditText) findViewById(R.id.search_input);
        searchResult = (RecyclerView) findViewById(R.id.search_result);

        SearchResultAdapter adapter = new SearchResultAdapter();
        searchResult.setLayoutManager(new LinearLayoutManager(this));
        searchResult.setAdapter(adapter);
        searchSubscription = RxTextView.afterTextChangeEvents(searchInput)
                // Convert the event to a String
                .map(textChangeEvent -> textChangeEvent.editable().toString())
                // Perform search on computation scheduler
                .subscribeOn(Schedulers.computation())
                // If we get multiple events within 200ms, just emit the last one
                .debounce(200, TimeUnit.MILLISECONDS)
                // "Convert" the query string to a search result
                .switchMap(this::searchNames)
                // Switch back to the main thread
                .observeOn(AndroidSchedulers.mainThread())
                // Set the result on our adapter
                .subscribe(adapter::setSearchResult);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (!searchSubscription.isDisposed()) {
            searchSubscription.dispose();
        }
    }

    private Observable<List<String>> searchNames(String query) {
        Log.d(TAG, "searchNames: Search for " + query);
        Log.d(TAG, "Searching on " + Thread.currentThread().getName());
        if (query == null || query.length() < MIN_LENGTH) {
            return Observable.just(Collections.emptyList());
        }

        LinkedList<String> result = new LinkedList<>();
        try (InputStream inputStream = getResources()
                .openRawResource(R.raw.unique_random_strings)) {
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader reader = new BufferedReader(inputStreamReader);
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.toLowerCase().contains(query.toLowerCase())) {
                    result.add(line);
                }
            }
        } catch (IOException e) {
            return Observable.error(e);
        }

        Collections.sort(result);
        Log.d(TAG, "searchNames: Found " + result.size() + " hits!");
        return Observable.just(result);
    }

    private class SearchResultAdapter extends RecyclerView.Adapter<SearchResultViewHolder> {
        private List<String> mResult;

        @Override
        public SearchResultViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View viewItem = View.inflate(InstantSearchActivity.this,
                    android.R.layout.simple_list_item_1, null);
            return new SearchResultViewHolder(viewItem);
        }

        @Override
        public void onBindViewHolder(SearchResultViewHolder holder, int position) {
            holder.textView.setText(mResult.get(position));
        }

        @Override
        public int getItemCount() {
            return mResult != null ? mResult.size() : 0;
        }

        public void setSearchResult(List<String> result) {
            mResult = result;
            notifyDataSetChanged();
        }
    }

    private class SearchResultViewHolder extends RecyclerView.ViewHolder {
        public final TextView textView;

        public SearchResultViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(android.R.id.text1);
        }
    }

}

and XML is here.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_margin="5dp"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/search_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:singleLine="true"
        android:inputType="text"
        android:hint="@string/search_hint"/>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/search_result"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/search_input"/>

</RelativeLayout>

Wrapping up: This is a great example of RxBinding and RxJava and you have some ideas of how to cut a lot of boilerplate code from your Android apps using RxJava to handle all of your application’s UI events. RxBinding simple to use provides a consistent API for consumption and makes your application much more composable and reactive.

Here is an example of form validation by using RxBinding and updated RxJava2 function called Flowable to handle the back pressure on UI. Please check this post Rx binding with Rx java and Rx android in Android.

In my next tutorial of this Rx series, we will learn new reactive feature and functions. In the Reactive world, we should be thinking about the reactive that removed all the boilerplate code. Here is the part-3 of the RxJava2 feature. If you wondering Kotlin for android then I would be recommended to check all this post of Kotlin Category.

Please do subscribe your email to get every newsletter from this blog and if you feel that this post helps you then do not forget to share and comment below.

Happy coding 🙂

0 0 votes
Article Rating
Understanding and practice of RxJava & RxBinding of Android part -2
Subscribe
Notify of
guest

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

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Omar Beshary

This is really great , thank you.

Rohit Sharma

In this code:
searchSubscription = RxTextView.afterTextChangeEvents(searchInput)
// Convert the event to a String
.map(textChangeEvent -> textChangeEvent.editable().toString())
// Perform search on computation scheduler
.observeOn(Schedulers.computation())
// If we get multiple events within 200ms, just emit the last one
.debounce(200, TimeUnit.MILLISECONDS)
// “Convert” the query string to a search result
.switchMap(this::searchNames)
// Switch back to the main thread
.observeOn(AndroidSchedulers.mainThread())
// Set the result on our adapter
.subscribe(adapter::setSearchResult);

.observeOn(Schedulers.computation()) and .observeOn(AndroidSchedulers.mainThread()) is wrong as instead of observeOn(Schedulers.computation()) it should be subscribeOn(Schdeu…);

Sunil Gupta

Yes Rohit, You are right. I update the code.

Scroll to top
3
0
Would love your thoughts, please comment.x
()
x