37

I have a BehaviorSubject that I would like to reset - by that I mean I want the latest value to not be available, just as if it was just created.

I don't seem to see an API to do this but I suppose there is another way to achieve the same result?

My desired behavior is that I need to emit events, and I'd like subscribers to get the latest event when they subscribe - if a particular manager is in a 'started' state. But when this manager is 'stopped' the latest event should not be available (just like if it was never started in the first place).

Denis Dmitrienko
  • 1,532
  • 2
  • 16
  • 26
BoD
  • 10,838
  • 6
  • 63
  • 59
  • With a BehaviorSubject there's a next value available when it's just created, that's the whole point of it (and why you have to supply that value when you create it). – jonrsharpe Aug 30 '17 at 12:14
  • 1
    @jonrsharpe But you don't have to supply it when you create it! http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/subjects/BehaviorSubject.html#create-- – BoD Aug 30 '17 at 12:17
  • Weird, it doesn't have that in the RxJS version (https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/behaviorsubject.md) - if you don't want to supply an initial value, why wouldn't you use a `ReplaySubject` instead? – jonrsharpe Aug 30 '17 at 12:19
  • I have the impression that BehaviorSubject is the equivalent of a ReplaySubject of size 1. But that doesn't answer my question at all :) – BoD Aug 30 '17 at 12:27
  • I think the answer is: you can't. But I was also initially confused by the premise of an empty behavior subject! – jonrsharpe Aug 30 '17 at 12:27
  • If you need the most recent item that has emitted by your Subject before you subscribed to it - you may use BehaviorSubject but cannot use ReplaySubject. And yes, it can be empty when created. The initial value is useful as a "default" value. – Denis Dmitrienko Jul 17 '19 at 07:48

6 Answers6

30

I assume you want to clear the BehaviorSubject (because otherwise don't call onComplete on it). That is not supported but you can achieve a similar effect by having a current value that is ignored by consumers:

public static final Object EMPTY = new Object();

BehaviorSubject<Object> subject = BehaviorSubject.createDefault(EMPTY);

Observable<YourType> obs = subject.filter(v -> v != EMPTY).cast(YourType.class);

obs.subscribe(System.out::println);

// send normal data
subject.onNext(1);
subject.onNext(2);

// clear the subject
subject.onNext(EMPTY);

// this should not print anything
obs.subscribe(System.out::println);
Bob Dalgleish
  • 8,167
  • 4
  • 32
  • 42
akarnokd
  • 69,132
  • 14
  • 157
  • 192
  • I could not use this solution, but it helped solving my issue as well: if you use generic classes like `List` e.g., the cast is not possible... Using an empty default object of the concrete class and filter with `v -> !v.equals(EMPTY)` and removing the cast should help in this case though. – prom85 Jul 10 '18 at 07:39
15

Another method of switching the value of an observable on and off is to use switchMap() to flip between the actual observable and an empty one.

Let's assume you have a manager object, and it has a observable that shows its state. Then,

subjectObservable = manager.getStateObservable()
  .switchMap( state -> state == ON ? subject : Observable.never() );

will only emit values while the manager is in the ON state.

Bob Dalgleish
  • 8,167
  • 4
  • 32
  • 42
3

Just use setTimeout like this:

setOtpoint(value) {

    this._setOption.next(value);

    // Clear BehaviorSubject after emit value

    setTimeout(() => {
      this._setOption.next(null);
    }, 100);

  }
  • i tried above code its working but after login again _setOption is get blank value and value is received after refresh – Kapil Soni Sep 26 '20 at 04:51
2

I find out a better solution for some cases and is:

subject.skiplast(1)

it can work to clean the last position on stream that is being retained because of the BehaviorSubject "behavior"

Sunil Garg
  • 14,608
  • 25
  • 132
  • 189
VITROXMAN
  • 216
  • 2
  • 5
1

A problem with @akarnokd's answer is that the .cast prevents YourType from being an interface or a generic type such as List<String>.

Another option is to filter on a boolean field that you can switch on and off.

    private BehaviorSubject<PandoraApp> subject = BehaviorSubject.create();
    private boolean enabled = true;

    Observable<PandoraApp> observable = subject.filter(v -> enabled);

If methods are being called on different threads you can use AtomicBoolean for the filter flag.

miguel
  • 16,205
  • 4
  • 53
  • 64
0

Here is my lib for this:

implementation "com.github.kolyall:rxjava2-empty:1.0.36"

Example:


private val myBehaviorSubject = BehaviorSubjectOptional.createOptional<MyItem?>()

errorBehaviorSubject.toObservable()
.subscribe{ item-> Log.d("onNext1", "item = $item")}

var item:MyItem? = MyItem()
myBehaviorSubject.onNextOptional(item)

//For reset:
myBehaviorSubject.clear()
//OR
item = null
myBehaviorSubject.onNextOptional(item)


errorBehaviorSubject.toObservable()
.subscribe{ item-> Log.d("onNext2", "item = $item")}
NickUnuchek
  • 11,794
  • 12
  • 98
  • 138