📜 ⬆️ ⬇️

Implementing Search Using RxJava

This article will discuss the optimal and compact implementation of search using RxJava for Android, eliminating unnecessary results and reducing the number of useless network calls.

Sample search
The original was written on October 16, 2017. Translation is free.

Currently, most of the applications that we use daily provide the ability to quickly search. Therefore, the implementation of the search is an important task. We, as developers, it is important to implement the search in the best way.

Consider how to implement search in the best way using RxJava. Do not forget, in RxJava there are operators for everything .
')
I personally believe that you can solve any problem very easily with RxJava, which can be very difficult without RxJava. RxJava is a great technology.

Take a look at the elements of RxJava, which we will use to implement the search:


Let's start


First you need to make SearchView observable. Let's make it using PublishSubject. I am using SearchView from Android. View can be any, with functionality as at EditText. To implement observable, you need to implement a listener to change the text in the field.

public class RxSearchObservable { public static Observable<String> fromView(SearchView searchView) { final PublishSubject<String> subject = PublishSubject.create(); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { subject.onComplete(); return true; } @Override public boolean onQueryTextChange(String text) { subject.onNext(text); return true; } }); return subject; } 
Note from ConstOrVar :
Observable for SearchView will not work correctly. You subscribe to it in onCreate (), but when an onQueryTextSubmit () is triggered, a reply will occur, since onComplete will be called. It turns out that the second search will not work. In order for the second search to work, you need to get rid of subject.onComplete ();
A note from Scrobot and BFS :
You should not use a Subject, it exists only for 1 goal: to combine the imperative style with the jet. Better to use Observable.create (). The search is exactly the case when you need to think about Backpressure, and since almost everyone today uses RxJava2, then this problem is solved there with the help of Flowable, and it is better to register this case for it.
Next, you need to call the created method and add operator calls, as in the example below.

 RxSearchObservable.fromView(searchView) .debounce(300, TimeUnit.MILLISECONDS) .filter(new Predicate<String>() { @Override public boolean test(String text) throws Exception { if (text.isEmpty()) { return false; } else { return true; } } }) .distinctUntilChanged() .switchMap(new Function<String, ObservableSource<String>>() { @Override public ObservableSource<String> apply(String query) throws Exception { return dataFromNetwork(query); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<String>() { @Override public void accept(String result) throws Exception { textViewResult.setText(result); } }); 

Now let's discuss why these operators are used and how they work together .

Debounce


Time parameters are transferred to this operator. In this case, it catches text input events by the user, for example, “a”, “ab”, “abc”. Typing often happens very quickly and is fraught with a large number of network calls. But the user is usually interested only in the results for "abc". So, you need to discard the execution of queries for "a", "ab". Operator debounce to the rescue. It expects user inactivity during the time passed in the parameter. If any input is made during the wait, the wait counter will be reset, the countdown will start again, the previous transmitted result, for example, “a” will be discarded. Thus, the debounce operator sends further along the chain only those elements that survived without invoking new events for the specified waiting time.
debounce example image

Filter


This operator is used to screen out unwanted lines, for example, empty lines, to avoid unnecessary network calls.

DistinctUntilChanged


This operator is used to avoid duplicate network calls. For example, the last search query was “abc”, then the user deleted “c” and re-entered “c”. The result is “abc” again. If the network call is already in process with the same parameter, then the distinctUntilChanged operator will not allow performing the same call again. Thus, the distinctUntilChanged operator filters out elements that are repeated successively passed to it.
distinctUntilChanged example image

Switchmap


In this example, this operator is used to exclude network calls, the results of which no longer need to be shown to the user. For example, the last search query was “ab” and there is a working network call for this query, but the user enters “abc” at this time. The result for “ab” is no longer needed, only the result for “abc” is needed. SwitchMap to the rescue. It passes on the results of only the last query, ignoring the rest.

Javadoc describes switchMap as follows:

Returns a new Observable , applying the passed function to each Observable element received, but passing on elements created only by the last Observable received.

We did it! Just imagine how difficult it would be to implement a search without RxJava.

If you want to see a complete example, take a look at the following project .

Source: https://habr.com/ru/post/345278/


All Articles