Search code examples
androidrx-java2rx-android

Why onNext() is updating textview, though observeOn(Schedulars.io()) on a different thread?


Observable.range(11,10).subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .subscribe(new Observer<Integer>() {
                @Override
                public void onSubscribe(@NonNull Disposable d) {

                }

                @Override
                public void onNext(@NonNull Integer integer) {
                    textView.setText(String.valueOf(integer));
                    Log.d(TAG, "onNext: "+Thread.currentThread().getName());
                }

                @Override
                public void onError(@NonNull Throwable e) {

                }

                @Override
                public void onComplete() {

                }
            });

onNext() supposed to run on separate thread, but how is it updating textview, which is on main thread?


Solution

  • It seems that at the very beginning of the lifetime of a view, there is a very short timespan where you are able to change the view off the main thread.

    As you started a thread off the main thread, directly in onCreate(), and this thread almost instantly returns a result (as there is no real work to do) you will not get a CalledFromWrongThreadException when you adjust the view.

    If you put a short delay (maybe it is different on your machine) - for me, 50ms was enough - before the work in the thread / Observable starts, you will see the expected CalledFromWrongThreadException.

    Observable.just("first")
      .subscribeOn(Schedulers.newThread())
      .delay(50, TimeUnit.MILLISECONDS)
      .observeOn(Schedulers.newThread())
      .subscribe(item -> {
        textView.setText(item); // after the delay you will get a CalledFromWrongThreadException
      });
    

    And this is not related to RxJava. Creating a Thread which updates the view immediately shows the same behavior:

    new Thread(new Runnable() {
      @Override
      public void run() {
        textView.setText("foo"); // no CalledFromWrongThreadException
      }
    }).start();
    

    Looks like this issue goes back to ViewRootImpl checkThread() which did not get called in this case. For further understanding follow the links below.

    Despite, any change to a view should happen from the main thread. The scenario you have shown seems like a "lucky" side-effect.

    Documentation