Building an AutoCompleting EditText
using RxJava
RxJava
can simplify the fairly complex interactions needed to build an autocompleting EditText
view in Android.
Examples of this interaction are commonplace; open your favourite browser on your phone and start typing in the search bar. You’ll likely see results automatically flash up as you type.

While a common enough feature, there are many suggested implementations on how this can be built, and there are many nuances to the problem. Here is one approach that seems to be working well.
I decided to use RxJava
as the crucial ingredient, and a regular old EditText and RecyclerView
to accept the input and display the results respectively.
The End Result
For a full working example, you’d best check out the source code for DuckDuckGo Android on GitHub.

The final result makes use of the following RxJava
concepts:
Activity
The Activity
is fairly light on code, as it should be. We add a TextWatcher
and pass through the new query to the ViewModel
on every change.
ViewModel
The ViewModel
has all the good bits!
AutoCompleteApi
This requires your implementation: from a query, you need to return an Observable
of autocomplete suggestions.
fun autoComplete(query: String): Observable<AutoCompleteResult>
You might obtain this from querying your DB. Or you might do as I do here in this project and make an HTTP request, using Retrofit
to return your result as an Observable
.
Breaking it Down
The two main parts that fit together to make this work:
- Limiting number of requests being made
- Canceling requests that are no longer useful
Limiting Number of Requests
Naive Approach (don’t do this)
A naive approach would be to fire an API call with every character change in the EditText
. However, the likelihood is that the users can type quickly enough that you can greatly reduce the number of API calls made by not searching while they are still adding more characters.
If the user is going to search for RxJava
there is little point in fetching autocomplete suggestions for R
when tens of milliseconds later, the user has typed more characters. Having API requests in flight simultaneously for R
, Rx
, RxJ, RxJa
and RxJav
will all contribute to slowing down showing the user the results they actually want to see.
Debouncing Requests (do this!)
We can make use of the RxJava debounce()
operator to delay taking any action until the user pauses briefly. So while the user continues to type letters, we wait. When the user has stopped typing for a long enough period of time, we then start the API request.
.debounce(300, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
You might decide to adjust the debounce period, but 300ms feels about right to me.
Use of distinctUntilChanged()
ensures that the user can search for the same thing twice, but not immediately back to back. There’s no point in cancelling a request to immediately start the same new request when we expect the results to be identical.

Canceling Previous Requests
The debouncing helps reduce the number of useless requests being made, but if the user pauses long enough for a request to start but then types again, we now have a request in flight which is ultimately pointless; by the time the result arrives back, the user isn’t interested in seeing it any longer as they’ve changed the query already.
By using a switch map, we can cancel a request which is no longer useful to us and start a request for the latest query instead.
The greatest feature of a switch map is its cancelling ability. When there is a new value received, it will unsubscribe from the existing subscription and create a new subscription for the new value.
In other words, when a new query is entered, we discard the existing autocomplete request (if there is one) and create a new autocomplete request.