0

Recently I started android project with hard usage of Reactive extensions. I've read some introductions and tutorials, but I'm still at beginner's level. According to this article:

everything is a stream

however my current understanding (or mental barrier) tells me that any operation which mutates state (removing data from repository for example) should not be/return a stream/observable.

Little background about my domain: I have a use case for registering geofences. Since geofences do not survive reboot, I keep track of active geofences in repository. Sometimes app needs to remove geofence, so basic steps to achieve this are:

  • retrieve geofence from repository
  • remove geofence from device
  • remove geofence from repository

my current solution is following:

geofenceRepository.get(id)
            .map(new Func1<Geofence, String>() {
                @Override
                public String call(Geofence geofence) {
                    geofenceRepository.delete(geofence.getId()); // synchronous call here
                    return geofence.getRequestId();
                }
            })
            .toList()
            .flatMap(new Func1<List<String>, Observable<Status>>() {
                @Override
                public Observable<Status> call(List<String> ids) {
                    return locationProvider.removeGeofences(ids);
                }
            });

where Geofence is my custom data structure and locationProvider is from this nice library.

You'll notice that data retrieval is implemented as stream/observable unlike delete.

What I don't like in above example is: map operator with side effect

Questions

  • What would be better solution to be more "reactive", what I'm missing here?
  • Does it make sense to use reactive approach at all?

by reactive programming I mean:

programming with asynchronous data streams

Community
  • 1
  • 1
tsobe
  • 179
  • 4
  • 14

2 Answers2

0

Reactive is great and I think this situation is perfect.

I think what you really want to do here is make sure each of your operators does exactly 1 thing. Like you said, the flatMap is also removing your geofence.

Try using the onNext operator in your chain for the removal. What you want to do it retrieve it, which it looks like geofenceRepository.get(id), remove it with an operator, then remove it from the locationProvider. Maybe something like:

geofenceRepository.get(id)
        .map(new Func1<Geofence, String>() {
            @Override
            public String call(Geofence geofence) {
                return geofence.getRequestId();
            }
        })
        .doOnNext(new Action1<String>){
          @Override
          public void call(final String geoFenceId) {
            geofenceRepository.delete(geofence.getId());
          }
        })
        .doOnNext(new Action1<String>() {
          @Override
          public void call(final String geoFenceId) {
                return locationProvider.removeGeofences(ids);
          }
        });

What you probably really want to do is create two subscribers. That way if you want to watch the status of one or both you can. You could combine the status of each. It depends a bit on if deleting from the repository and deleting from the provider are independent.

Observable<String> getFence = geofenceRepository.get(id)
        .map(new Func1<Geofence, String>() {
            @Override
            public String call(Geofence geofence) {
                return geofence.getRequestId();
            }
        });

        getFence.subscribe(new Action1<String>){
          @Override
          public void call(final String geoFenceId) {
            geofenceRepository.delete(geofence.getId());
          }
        });

        getFence.map(new Func1<String, Status>() {
          @Override
          public Status call(final String geoFenceId) {
                return locationProvider.removeGeofences(ids);
          }
        }).subscribe(new Action1<Status>(){
           @Override
           public void call(final Status status(){
              //Handle your status for each removal
           }
        });
Vinny K
  • 194
  • 15
0

I don't see any problem with your approach and being more reactive would mean more API uses/returns Observables. You can have side-effects in any of the lambdas but be careful when you mutate a value since if asynchrony is involved, the same object may be mutated at the same time at different stages of the pipeline. Usually, we use immutable or effectively immutable values to avoid this problem. There is no real need to split your activities so the suggested doOnNext separation is a preference of the particular developer

If your geofenceRepository.delete had a version that returns an Observable of some sort, you could go more reactive by flatMapping over it:

get(id)
.flatMap(f -> geoFence.deleteAsync(f.getId()).map(v -> f.getRequestId()))
.toList()
.flatMap(...)
.subscribe(...)

Here, deleteAsync would return an Observable<Void> which when completes, will resume the main sequence with the requestId.

akarnokd
  • 69,132
  • 14
  • 157
  • 192