RxJava takeuntil运算符抛出IllegalStateException

时间:2018-07-19 08:22:22

标签: android rx-java

我正在尝试使用RxJava实现一个简单的自动完成视图, 按下取消按钮可以取消后端通话。 这是我的代码:

    Observable<String> searchButton$ = RxView
                                        .clicks(searchButton)
                                        .map(item -> mEditText.getText().toString());
    Observable<Integer> cancelRequest$ = RxView
                                        .clicks(cancelRequest)
                                        .scan(0,(counter,string) -> ++counter)
                                        .skip(1);

    Observable<String> search$ = searchButton$
                                            .distinctUntilChanged()
                                            .debounce(1,TimeUnit.SECONDS)
                                            .doOnNext(text-> {
                                                Log.i(TAG,"Searching ["+text+"]");
                                            });
    search$
        .observeOn(Schedulers.single())
        .doOnError(error -> {
            Log.d(TAG,"Error after ObserveOn");
            error.printStackTrace();
        })
        .switchMap(text ->
                fetchList(text)
                .doOnError(error -> {
                    Log.d(TAG,"Error after FetchList");
                    error.printStackTrace();
                })
                .takeUntil(cancelRequest$)
                .doOnError(error -> {
                    Log.d(TAG,"Error after TakeUntil");
                    error.printStackTrace();
                })
        )
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(list -> {
            Log.i(TAG,"Fetched:"+ list.toString());
            resultView.setText("");
            list.forEach(item -> resultView.append(item + "\n"));
        });

但是在编辑文本中输入内容时,这就是我得到的输出

07-19 13:42:14.881 13869-13907/personal.com.actrecog I/MainActivity: Searching [12]
07-19 13:42:14.883 13869-13909/personal.com.actrecog D/MainActivity: Error after TakeUntil
07-19 13:42:14.883 13869-13909/personal.com.actrecog W/System.err: java.lang.IllegalStateException: Expected to be called on the main thread but was RxSingleScheduler-1

我不明白为什么takeuntil期望在mainThread上被调用。 我尝试在takeUntil之前添加ObserveOn(AndroidSchedulers.mainThread()),但错误仍然相同。

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

如果您深入研究RxBinding代码,就会找到答案。

clicks定义:

 /**
   * Create an observable which emits on {@code view} click events. The emitted value is
   * unspecified and should only be used as notification.
   * <p>
   * <em>Warning:</em> The created observable keeps a strong reference to {@code view}. Unsubscribe
   * to free this reference.
   * <p>
   * <em>Warning:</em> The created observable uses {@link View#setOnClickListener} to observe
   * clicks. Only one observable can be used for a view at a time.
   */
  @CheckResult @NonNull
  public static Observable<Object> clicks(@NonNull View view) {
    checkNotNull(view, "view == null");
    return new ViewClickObservable(view);
  }

ViewClickObservable定义:

final class ViewClickObservable extends Observable<Object> {
  private final View view;

  ViewClickObservable(View view) {
    this.view = view;
  }

  @Override protected void subscribeActual(Observer<? super Object> observer) {
    if (!checkMainThread(observer)) {
      return;
    }
    Listener listener = new Listener(view, observer);
    observer.onSubscribe(listener);
    view.setOnClickListener(listener);
  }

  static final class Listener extends MainThreadDisposable implements OnClickListener {
    private final View view;
    private final Observer<? super Object> observer;

    Listener(View view, Observer<? super Object> observer) {
      this.view = view;
      this.observer = observer;
    }

    @Override public void onClick(View v) {
      if (!isDisposed()) {
        observer.onNext(Notification.INSTANCE);
      }
    }

    @Override protected void onDispose() {
      view.setOnClickListener(null);
    }
  }
}

subscribeActual方法中,您会注意到!checkMainThread(observer)具有以下定义:

public static boolean checkMainThread(Observer<?> observer) {
    if (Looper.myLooper() != Looper.getMainLooper()) {
      observer.onSubscribe(Disposables.empty());
      observer.onError(new IllegalStateException(
          "Expected to be called on the main thread but was " + Thread.currentThread().getName()));
      return false;
    }
    return true;
  }

换句话说,RxView.clicks需要在主线程而不是takeUntil上进行预订。 observeOn()表示您要观察在给定调度程序上的结果。在您的情况下,您必须使实际工作发生在主线程上。在这种情况下,observeOn()将无济于事,您必须使用subscribeOn()

.switchMap(text ->
                fetchList(text)
                .doOnError(error -> {
                    Log.d(TAG,"Error after FetchList");
                    error.printStackTrace();
                })
                .takeUntil(cancelRequest$)
                .subscribeOn(AndroidSchedulers.mainThread())
                .doOnError(error -> {
                    Log.d(TAG,"Error after TakeUntil");
                    error.printStackTrace();
                })
        )