0

I am using the Android ReactiveLocation library to receive regular location updates. I want to keep receiving location updates even if nothing in my app is using them, so that I always have an up-to-date location I can use immediately if I need to.

This is how I start getting location updates in a core component of my app. I want to republish the latest value, and any new detected locations, to any subscriber further down the chain in my app, and this is what the replay(1) is for.

locationProvider = new ReactiveLocationProvider(context);
locationObservable = locationProvider.getUpdatedLocation(locationRequest)
.replay(1);

Elsewhere in the app, I subscribe to this republished obervable:

locationSubscription = locationObservable
        .filter(new Func1<Location, Boolean>() {
            @Override
            public Boolean call(Location location) {
                return location.getAccuracy() < LOCATION_ACCURACY_THRESHOLD;
            }
        })
        .subscribe(new Action1<Location>() {
            @Override
            public void call(Location location) {
            }
        });

This appears to do the job: my eventual subscriber gets the latest location returned immediately, and continues to receive new updates, but I want to make sure I'm not building up a huge buffer of unused locations somewhere in the chain when the final subscriber is not subscribed. I'm an Rx noob. How does backpressure apply to this situation? Is replay(1) doing what I expect, and discarding all unwanted locations apart from the latest one?

Graham Borland
  • 60,055
  • 21
  • 138
  • 179

1 Answers1

1

There are several factors that come into play with replay. replay(1) does backpressure coordination and replays values as requested by the subscriber.

This means that if the subscriber is behind an async boundary, replay will start accumulating values. The rate at which this happens is dependent on the the current set of subscribers: the operator requests the maximum of what subscribers requested so mixing bounded and unbounded subscribers may lead to buffer bloat if they consume at a different rate.

You can think of its buffer as a singly linked list where each subscriber points to a node in that list as well as the operator pointing to just before the end of the list. Due to the single links, if there are no subscribers, the front of the list can be garbage collected.

If there was a large requester which unsubscribed, replay() will "drop" the excess values and continue holding onto 1 element at a time, slowly "dripping away".

The alternative is to use BehaviorSubject which will remember the very last value and doesn't do backpressure at all so no risk of buffer bloat but risk of MissingBackpressureException.

Edit:

Actually, I think there is a backpressure bug within replay() so cold sources may not work properly.

akarnokd
  • 69,132
  • 14
  • 157
  • 192
  • So if I understand you correctly, if everything is happening on Android's main thread (no scheduler switching - is that what you mean by "async boundary"?) and I only have one subscriber, then I think it is doing what I want. – Graham Borland Oct 17 '15 at 08:24
  • Async boundary is (mostly) when you apply an operator with a Scheduler parameter – akarnokd Oct 17 '15 at 08:26