3

This is a conceptual problem with reactive programming due to my misunderstanding, I think.

I have an Observable that issues a network call returning JSON, which I filter(), looking at the JSON for an admin flag, and then perform actions in the subscribe() method.

observable
.filter((json) -> { return json.isAdmin; })
.subscribe((json) -> { /* Do things for admin */ });

I also want to do the same, but when the user is not an admin:

observable
.filter((json) -> { return !json.isAdmin; })
.subscribe((json) -> { /* Do things for non-admin */ });

I am now aware if I run the above two code block, the network call will be twice called.

I know I can put an if statement in a single subscribe() method, but I'm not sure if this is the reactive way.

Is there a way where I can keep the above structure but only set the observable in motion once--i.e. one network call not two?

It seems as if I want to react in multiple ways to an Observable's result, without making that Observable run multiple times.

mmm111mmm
  • 3,607
  • 4
  • 27
  • 44

1 Answers1

3

There are two big categories of observables: hot and cold ones. An observable that you describe, whose generating part executes on each subscribe() call, is known as cold. On the other hand a hot observable normally emits items irrespective of clients subscribing and unsubscribing from it.

For your use case a family of operators may be suitable: connect, publish, share, etc.

An example:

ConnectableObservable</*Your type*/> connectable = Observable.just(1, 2, 3).publish();

connectable
    .filter((json) -> { return json.isAdmin; })
    .subscribe((json) -> { /* Do things for admin */ });

connectable
    .filter((json) -> { return !json.isAdmin; })
    .subscribe((json) -> { /* Do things for non-admin */ });

connectable.connect(); // here's when sharedObservable starts emitting items

There's a beta operator autoConnect() that automatically connects to the observable when the specified number of clients is subscribed:

Observable<Integer> autoObservable = Observable.just(1, 2, 3).publish().autoConnect(2);
autoObservable.subscribe();
autoObservable.subscribe(); // here's when autoObservable starts emitting items

I have to mention the share() operator which is also frequently used and is a shortcut for .publish().refCount(). From its javadoc:

Returns a new Observable that multicasts (shares) the original Observable. As long as there is at least one Subscriber this Observable will be subscribed and emitting data. When all subscribers have unsubscribed it will unsubscribe from the source Observable.

Observable<Integer> shared = Observable.just(1, 2, 3).share();
shared.subscribe(); // starts emitting here...
shared.subscribe(); // ... but is not triggered again, only starts listening for new emissions
// (potential race condition regarding the first emitted item)
AndroidEx
  • 15,524
  • 9
  • 54
  • 50
  • Thanks. That's wonderful. I'll have a play around later, but this looks to be exactly what I was missing. Do you have any recommendations on how to better learn RxJava etc apart from the plethora of beginners tutorials I see? – mmm111mmm Mar 03 '16 at 13:16
  • @newfivefour I think you're already on the right track asking this type of questions. Official docs are pretty good. [This page on operators](http://reactivex.io/documentation/operators.html) is great, even has a decision tree to help you pick a suitable operator in different cases. [Subjects](http://reactivex.io/documentation/subject.html) ("hot" observables) is also an important concept, highly recommend to read and use. – AndroidEx Mar 03 '16 at 13:59
  • Thanks. That page was useful. I'll have to get my head around the marble diagrams... Thanks again. – mmm111mmm Mar 04 '16 at 15:15