Winning race conditions with ReplaySubject

How I won the race with ReplaySubject … 🏃

Replay this 🔉 🎵 …

Hello fellow developers …

Everybody knows how cool RxJava is and how it can help you easily solve concurrency issues or race conditions in Android applications. Rxjava requires a paradigm shift away from the normal imperative approach. It is reactive based, where bits and pieces of code aren’t executed until it knows it’s end mean. Today I am going to bring you a problem and solution using RxJava’s ReplaySubject, that for some, may not be quite straight forward.

If you have never heard of Subjects in RxJava you can find a quick description below of what it is and how it works.

A Subject is a sort of bridge or proxy that is available in some implementations of ReactiveX that acts both as an observer and as an Observable. Because it is an observer, it can subscribe to one or more Observables, and because it is an Observable, it can pass through the items it observes by re-emitting them, and it can also emit new items.

Normally, in RxJava, what you would do is to have an API call return an observable to which you can subscribe to and respond to accordingly by updating your UI. What if you can’t make that API call return an observable or you have some sort of race condition issue? This problem can easily be solved with the use of RxSubjects. Let me explain.

Lets say you want a map to be shown on a fragment. Following the Android documentation this is how you would do it:

@Override

protected void onCreate(Bundle savedInstanceState) {

....

MapFragment mapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.map);

mapFragment.getMapAsync(this);

}



@Override

public void onMapReady(GoogleMap map) {

//Map is ready. Add markers to map

}

Under the hood getMapAsync makes an asynchronous call to download the map, map tiles and other information.

Now lets say you want to make a separate call to a back-end service to fetch a list of points of interest (POIs). Assuming you are using an Model-View-Presenter (MVP) architecture in your app this call would be made first thing when the presenter starts because we want the information to be shown to the user immediately when he lands on the screen. This list contains not only latitudes and longitudes to be shown on the map but also other information which I want to display in a BottomSheet. For this call we can use simple RxJava in our Presenter as we would normally do.

@Override

public void getPointsOfInterest() {

mLocationsDataManager.getPointsOfInterest()

.observeOn(AndroidSchedulers.mainThread())

.subscribeOn(Schedulers.io())

.subscribe(list -> { mView.showPointsOfInterest(list)});

And in our view we would have the callback for showing the POIs in the map and in the BottomSheet like this:

@Override

public void showPointsOfInterest(ArrayList<POI> pois) {

//add poi info to bottom sheet and at lat lngs to map

}

Now how would you add the markers to the map? You might think “I’ll just save the map object and in the showPointsOfInterest method add the markers to the map and the information to the bottomsheet”. But what if the map isn’t ready when our call has finished? Map object will be null and therefore - 💣- NullPointerException. You could ultimately add a null check but it wont solve our problem. If you do this and map is null you wont see the markers in the map.

You could also think, just make the call for getting the POIs when the map is ready. However, we don’t want this either, as we still have a requirement to show the location information even if the map fails to load and as soon as possible. We don’t want the user to wait for the map to be ready.

Another option you might desperately consider is to make a second getPointsOfInterest call in the onMapReady callback. But we don’t want to make a second redundant call.

So we’ve encountered ourselves with a race condition.