Search code examples
androidrx-androidrx-java2rx-binding

RxJava filter behaviour


I wrote code which is debouncing text in EditText and filtering out text with lest than 4 characters:

charSequenceInitialValueObservable.skipInitialValue()
    .filter(new Predicate<CharSequence>() {
      @Override public boolean test(@NonNull CharSequence charSequence) throws Exception {
        Log.d("TAG", "l: " + charSequence.length());
        return charSequence.length() > 3;
      }
    })
    .doOnNext(new Consumer<CharSequence>() {
      @Override public void accept(@NonNull CharSequence charSequence) throws Exception {
        Log.d("TAG", "1. doOnNext: \"" + charSequence + "\"");
      }
    })
    .debounce(300, TimeUnit.MILLISECONDS)
    .doOnNext(new Consumer<CharSequence>() {
      @Override public void accept(@NonNull CharSequence charSequence) throws Exception {
        Log.d("TAG", "2. doOnNext: \"" + charSequence + "\"");
      }
    })
    .subscribeWith(new DisposableObserver<CharSequence>() {
      @Override
      public void onNext(@io.reactivex.annotations.NonNull CharSequence charSequence) {
        Log.d("TAG", "onNext: \"" + charSequence + "\"");
      }

      @Override public void onError(@io.reactivex.annotations.NonNull Throwable e) {
        Log.d("TAG", "onError: " + e.toString());
      }

      @Override public void onComplete() {
        Log.d("TAG", "onComplete");
      }
    });

When user is entering text - filtering works fine, but when user is using backspace, last empty char sequence is delivered to Subscriber:

1. doOnNext: "test hhu"
l: 7
1. doOnNext: "test hh"
l: 6
1. doOnNext: "test h"
l: 5
1. doOnNext: "test "
l: 4
1. doOnNext: "test"
l: 3
l: 2
l: 1
l: 0
2. doOnNext: ""
onNext: ""

Is this expected behaviour? Should I use filter operator second time after debounce? How to filter out it properly?


Solution

  • Debounce passes through the last value after no value is passed for the given timeout, so in theory it should be passing through "test". However the only value that gets through is "" which probably indicates that the value is not immutable and gets overwritten before it passes through the debounce operator.

    To fix this, make sure that charSequenceInitialValueObservable only receives immutable values or make a defensive copy before the debounce. (Something like .map(cs -> new String(cs)))