RxJava2 .subscribeOn .observeOn混乱。在主线程上运行

时间:2018-01-19 10:59:52

标签: android multithreading observable rx-java2

我有一个调用web服务的方法,我认为它正在IO线程上运行,直到服务停止并且UI冻结。

所以我开始了一些简单的测试来检查线程

implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.1.8'


public void test() {

    disposableRx.add(
            Observable.just(1, 2)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .doOnNext(new Consumer<Integer>() {
                        @Override
                        public void accept(Integer integer) throws Exception {
                            System.out.println("Emitting item on: " + Thread.currentThread().getName());
                        }
                    })
                    .map(new Function<Integer, Integer>() {
                        @Override
                        public Integer apply(@NonNull Integer integer) throws Exception {
                            System.out.println("Processing item on: " + Thread.currentThread().getName());
                            return integer * 2;
                        }
                    })

                    .subscribeWith(new DisposableObserver<Integer>() {
                        @Override
                        public void onNext(@NonNull Integer integer) {
                            System.out.println("Consuming item on: " + Thread.currentThread().getName());
                        }

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

                        @Override
                        public void onComplete() {
                        }
                    })
    );
}

导致以下输出指示主线程上的所有内容都在运行,尽管已订阅并观察?

Emitting item on: main
Processing item on: main
Consuming item on: main
Emitting item on: main
Processing item on: main
Consuming item on: main

BUT 如果我将observeOn移动到.subscribeWith之前,如下所示......

public void test() {

    disposableRx.add(
            Observable.just(1, 2)
                    .subscribeOn(Schedulers.io())
                    .doOnNext(new Consumer<Integer>() {
                        @Override
                        public void accept(Integer integer) throws Exception {
                            System.out.println("Emitting item on: " + Thread.currentThread().getName());
                        }
                    })
                    .map(new Function<Integer, Integer>() {
                        @Override
                        public Integer apply(@NonNull Integer integer) throws Exception {
                            System.out.println("Processing item on: " + Thread.currentThread().getName());
                            return integer * 2;
                        }
                    })
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribeWith(new DisposableObserver<Integer>() {
                        @Override
                        public void onNext(@NonNull Integer integer) {
                            System.out.println("Consuming item on: " + Thread.currentThread().getName());
                        }

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

                        @Override
                        public void onComplete() {
                        }
                    })
    );
}

输出是我正在寻找的东西,即使在阅读了许多关于RxJava的博客后,我必须说这让我感到困惑。

Emitting item on: RxCachedThreadScheduler-1
Processing item on: RxCachedThreadScheduler-1
Emitting item on: RxCachedThreadScheduler-1
Processing item on: RxCachedThreadScheduler-1
Consuming item on: main
Consuming item on: main

我已经删除了我的原始方法,直到它几乎是博客文章中的示例方法的副本

Multithreading like a boss

这意味着这应该在IO线程上运行loadPerson(),在主线程上发出。它没有。

disposableRx.add(
        repo.loadPersonProfile(id).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(new DisposableMaybeObserver<String>() {

                    @Override
                    public void onSuccess(@NonNull String response) {
                        loadPersonDetailsResponse.setValue(ViewModelResponse.success(response));
                        isLoading.setValue(false);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        loadPersonDetailsResponse.setValue(ViewModelResponse.error(e));
                        isLoading.setValue(false);
                    }

                    @Override
                    public void onComplete() {

                    }
                })
);

从我的方法中转出线程表明它在主线程上运行?

造成这种情况的原因是什么?

2 个答案:

答案 0 :(得分:6)

订单,其中observeOn()sunbscribeOn()以及其他运营商非常重要

  • subscribeOn()运算符告诉源Observable 要发出和转换项目的线程。

  • 小心放置observeOn()运算符的位置,因为它会发生变化 执行工作的线程!在大多数情况下,您可能想要延迟 切换到观察线程直到你的Rx链的最后。

    Observable.just("long", "longer", "longest")
        .subscribeOn(Schedulers.computation())
        .observeOn(AndroidSchedulers.mainThread())
        .map(String::length)
        .filter(length -> length == 6)
        .subscribe(length -> System.out.println("item length " + length));
    

这里的地图,过滤和消费是在主线程

中执行的
    observeOn()之前
  • map() 没有理由在map()运算符上方应用observeOn()运算符。 实际上,这段代码会导致NetworkOnMainThreadException!我们不想在主线程上读取HTTP响应 - 它应该在我们切换回主线程之前完成。

  • 您还可以使用多个observeOn()来切换此示例中的线程。

     Observable.just("long", "longer", "longest")
    .doOnNext(s -> System.out.println("first doOnNext: processing item on thread " + Thread.currentThread().getName()))
    .observeOn(Schedulers.computation())
    .map(String::toString)
    .doOnNext(s -> System.out.println("second doOnNext: processing item on thread " + Thread.currentThread().getName()))
    .observeOn(Schedulers.io())
    .map(String::toString)
    .subscribeOn(Schedulers.newThread())
    .map(String::length)
    .subscribe(length -> System.out.println("received item length " + length + " on thread " + Thread.currentThread().getName()));
    

输出:

first doOnNext: processing item on thread RxNewThreadScheduler-1
first doOnNext: processing item on thread RxNewThreadScheduler-1
first doOnNext: processing item on thread RxNewThreadScheduler-1
second doOnNext: processing item on thread RxComputationThreadPool-1
second doOnNext: processing item on thread RxComputationThreadPool-1
second doOnNext: processing item on thread RxComputationThreadPool-1
received item length 4 on thread RxCachedThreadScheduler-1
received item length 6 on thread RxCachedThreadScheduler-1
received item length 7 on thread RxCachedThreadScheduler-1

注意 根据此answer subscribeOn()不适用于下游运算符,因此它不保证您的操作将在不同的线程上。

  

subscribeOn效果向上游靠近源头   事件

至于你的问题,我做了一个测试,这是结果

   private void testRxJava2Async() {
 io.reactivex.Observable.fromCallable(new Callable<String>() {
        @Override
        public String call() throws Exception {

            Log.d(TAG,"callable (expensive assync method) was called on --> "+Thread.currentThread().getName());

            return null;
        }
    })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeWith(new Observer<String>() {


                @Override
                public void onSubscribe(Disposable d) {

                }

                @Override
                public void onNext(String s) {

                    Log.d(TAG,"onNext() was called on --> "+Thread.currentThread().getName());

                }

                @Override
                public void onError(Throwable e) {

                }

                @Override
                public void onComplete() {

                }
            });
}

测试结果:

callable (expensive assync method) was called on --> RxCachedThreadScheduler-1
onNext() was called on--> main

答案 1 :(得分:2)

相信我,我理解你的困惑,让我一步一步解释。请记住,每次订阅都应该遵守。我们都知道所有订阅都应该在工作线程(IO)和主线程(UI)上的观察者上执行。在第一个例子中,订阅被触发,然后你说,对于这个订阅,所有更新都将在主线程上传递,无论在任何转换期间是否有任何订阅。但是在第二个例子中,这就是你所说的,只有当订阅的地图转换被转换然后在主线程上运行时才会观察。想一想,编程中的前后增量。

希望这是有道理的。 RX框架很棒。但实际上需要现场练习才能做到正确。你见过第一手了。

要确保在主线程上推送和执行更改,您需要执行的是添加中间步骤以观察更改。

subscribeOn

这个想法是observeOn只接受开始处理,例如NetworkRequest,但不保证将值推送到同一个线程。 resource odbc_connect ( string $dsn , string $user , string $password [, int $cursor_type ] ) <?php // Microsoft SQL Server using the SQL Native Client 10.0 ODBC Driver - allows connection to SQL 7, 2000, 2005 and 2008 $connection = odbc_connect("Driver={SQL Server Native Client 10.0};Server=$server;Database=$database;", $user, $password); // Microsoft Access $connection = odbc_connect("Driver={Microsoft Access Driver (*.mdb)};Dbq=$mdbFilename", $user, $password); // Microsoft Excel $excelFile = realpath('C:/ExcelData.xls'); $excelDir = dirname($excelFile); $connection = odbc_connect("Driver={Microsoft Excel Driver (*.xls)};DriverId=790;Dbq=$excelFile;DefaultDir=$excelDir" , '', ''); ?> 嘿,我可以收到您最初订阅的那些值。因此,为了确保在主线程上转换值,您可以观察另一个纯线程的更改,执行操作(Map | Flatmap等),然后观察这些更改,这将保证只有那些值放在主线程上。基本上你的意思就是这个,嘿执行那些线程中所有繁重的处理,但是嘿嘿计算完成后,在主线程中将这些值传递给我继续我的工作,只有这样才能接受,因此没有工作会永远都要在主线上完成。