RxJava can seem removed from the world of user experience. It’s easy to think of it as a tool for back-end only. Today we’ll look at a real front-end problem, how to apply marble diagrams to solve it, and how to use Kotlin to clean up the solution. We’ll also look at the difference between throttling and debouncing, and some lesser-know overloads of debounce and delay .

The UX problem

We’re often trying to avoid flicker and flashes on Android apps. One common scenario where this can occur is loading from an API call. We want to show a loading animation if the API call takes too long. But if the API call is almost instantaneous, we don’t want to flicker between a quick loading animation and the content of the page.

There are some existing tools for getting around this, for example the ContentLoadingProgressBar in the Android SDK. Of course, top-tier apps have placeholder views instead of progress bars:

An example of a placeholder animation courtesy of www.androidhive.info

These types of views are best composed in a RecyclerView. The RecyclerView will manage the scheduling of UI updates to avoid the jarring effect when a View is suddenly inserted into a ViewGroup. But this is often not enough to prevent a flicker, flash, or jarring animation:

Since we’re just putting a data into a RecyclerView.Adapter , how do we avoid the flicker of a loading animation for a quick load?

The current feature is a suburb picker to specify pickup location for users selling items on Trade Me.

The shimmering placeholder for choosing pickup location in Android

In the implementation, the Fragments are humble views that simply subscribe to streams of content when they come into focus. The Activity publishes the content for the display, with the Fragment simply subscribing to updates in content. Note the publisher here could also be a ViewModel or a controller Fragment.

Since the published content is received as a reactive streams, we should easily be able to find a combinator on Observable to remove the flicker.

Two false starts

Marble diagram for Observable#throttleLast

Observable#throttleLast seems like a good candidate. Looking at the above marble diagram, imagine that:

the red marble represents our “still loading” state

the orange marble represents our loaded state

the other colored marbles represent UI updates from user interactions

Intervals of the delay parameter are represented by the clocks on the diagram.

The marbles at the top represent a stream of events as input into the box, with time flowing from left to right. The box is a function on Observable and the marbles at the bottom represent the resultant stream of events after the function has been applied.

Applying the marble diagram for throttleLast to our use case

Since the red marble (loading) was emitted before the tick of the clock, it is ignored and only the orange marble (loaded) is emitted.This is similar to the result we want: if the loading state is too quick we want it to be dropped and invisible to the user.

However, throttleLast is not quite right for the subsequent events. Imagine that the yellow and green marbles are events triggered by a user interaction e.g., clicking one of the items in the RecyclerView .

The yellow marble is ignored, which may be desirable since it occurred quite quickly before the green one. But the emission of the green marble is delayed until another tick of the clock. This is not what we want — we would prefer the green marble to be emitted immediately so that there is no delay between the user interaction (the click) and the result on screen (the change in the UI).

Since we’ve ruled out Observable#throttleLast , why not look at Observable#debounce ? This certainly sounds more like what we want to achieve. The first overload has a signature that looks like this:

The marble diagram looks like this:

Marble diagram for the first overload of debounce

Just from the diagram, we can see that the marbles are only being emitted after the tick of the clock. This is the same mismatch of use case we found for throttleLast .

What we really want is something that can debounce only in the case of loading, and leaves the other marbles to fall immediately.

A promising solution

Turns out there is an overload of debounce that achieves just this:

The combinator in the box shows three arrows of lengths 1, 2, and 3 for f(1) , f(2) , and f(3) . These represent three different debounce intervals. This shows we can achieve debouncing determined by a function, closer to what we want.

A concrete example is found in this Stack Overflow answer by Lawrence Kesteloot:

String[] items = {"one", "two", "three", "four", "five", "six"};

Observable.from(items)

.debounce(item -> item.equals("one")

? Observable.empty()

: Observable.timer(1, TimeUnit.SECONDS));

This clearly explains the parameter debounceSelector : it’s a function that takes as input T and outputs an Observable representing a debounce interval.

Now we can do this:

We can use the Extract Method refactor and type aliases to make this a little bit cleaner:

If it’s something we might use in more than a few places in the app, we could easily write an extension method to take the abstraction further:

Then we can use it like this:

We can write unit tests against the extension function using the TestScheduler since it allows fast-forwarding of time to see which elements have been emitted. We can also use an overload of Observable#delay to prepare the test data. It’s very similar to the overload of Observable#debounce we were using earlier:

The complete set of unit tests is below. Note how we can model our use cases exactly:

The final feature without flicker:

David Rawson is an Android Developer at Trade Me. You can find him on Stack Overflow here.