web-dev-qa-db-ja.com

subscribeOn()が別のスレッドで呼び出されても、Observableはメインスレッドで実行されます

活動の1つで奇妙な問題が発生しました。写真/ビデオの撮影から戻ったときに、onActivityResultに、ユーザーにカメラの名前を付けるダイアログが表示されます。ユーザーが[OK]を押すと、onNext()を、ファイルをコピーする(および進行状況ダイアログを表示する)要求されたファイル名を持つサブジェクトに送信します。

何らかの理由で、map()を呼び出しても、コピーを行うsubscribeOn(Schedulers.io())関数は常にメインスレッドで呼び出されます。

_@Override
protected void onActivityResult(final int requestCode, int resultCode, Intent intent) {
    ...

    final PublishSubject<String> subject = PublishSubject.create();`

    mSubscription = subject
            .subscribeOn(Schedulers.io())
            .map(new Func1<String, String>() {
                @Override
                public String call(String fileName) {
                    Log.I.d(TAG,"map");
                    return doSomeIOHeavyFuncition();
                }
            })
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Action1<String>() {
                @Override
                public void call(final String fullPath) {
                    Log.d(TAG,"onNext");
                    doSomethingOnUI(fullPath);

                    subject.onCompleted();
                }
            }, new Action1<Throwable>() {
                @Override
                public void call(Throwable throwable) {
                    ...
                }
            }, new Action0() {
                @Override
                public void call() {
                    ...
                }
            });

    final AlertDialog dialog = new AlertDialog.Builder
    ....
    .create()
            .show();

    dialog.getButton(DialogInterface.BUTTON_POSITIVE)
            .setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    String someString = getStringFromDialog(dialog);

                    dialog.dismiss();
                    InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(input.getWindowToken(), 0);

                    showProgressDialog();
                    subject.onNext(someString);
                }
            });
}
_

subscribeOn(Schedulers.io())呼び出しをobserveOn(Schedulers.io())に変更すると、問題が解決しました。それでも、なぜそれがうまくいかなかったのか知りたいです...

18
Yoshkebab

subscribeOnobserveOnは、ほとんど混乱している演算子です。前者は、指定されたスケジューラー(スレッド)でサブスクリプションの副作用が発生することを確認しますが、そのスレッドで値がポップアップするという意味ではありません。

たとえば、Observerがサブスクライブするときにネットワーク接続を開く場合、メインスレッドで実行したくないので、subscribeOnがそのサブスクリプションとネットワーク接続が作成される場所を指定する必要があります。

最終的にデータが到着すると、発信スレッドは何でも、スケジューラーの1つでも、バックグラウンドのプレーンな古いスレッドでもかまいません。私たちはそのスレッドを知らないか好きではないので、データの観測を別のスレッドに移動したいと思います。これはobserveOnの動作です。指定されたスケジューラでonNextロジックを実行した後のオペレーターを確認します。 Android開発者は既にそれを使用して、値の監視をメインスレッドに戻しています。

しかし、めったに説明されないのは、最終結果が再びメインスレッドに到達する前に、メインスレッドから追加の計算を行いたい場合に起こることです。複数のobserveOn演算子を使用します。

source
.observeOn(Schedulers.computation())
.map(v -> heavyCalculation(v))
.observeOn(Schedulers.io())
.doOnNext(v -> { saveToDB(v); })
.observeOn(AndroidSchedulers.mainThread())
...
47
akarnokd