web-dev-qa-db-fra.com

Les exécutions observables sur le thread principal même si subscribeOn () est appelée sur un autre thread

J'ai eu un problème bizarre dans l'une de mes activités. En revenant de prendre une photo/vidéo, dans mon onActivityResult je montre une boîte de dialogue qui permet à l'utilisateur de nommer la caméra. Une fois que l'utilisateur a appuyé sur OK, j'envoie onNext() à un sujet avec le nom de fichier demandé qui copie le fichier (et affiche la boîte de dialogue de progression).

Pour une raison quelconque, la fonction map() qui effectue la copie est toujours appelée sur le thread principal, même si j'appelle 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);
                }
            });
}

La modification de l'appel de subscribeOn(Schedulers.io()) en observeOn(Schedulers.io()) a résolu le problème. Je voudrais quand même savoir pourquoi cela n'a pas fonctionné ...

18
Yoshkebab

subscribeOn et observeOn sont les opérateurs les plus confus qui existent. Le premier s'assure que les effets secondaires de l'abonnement se produisent sur le planificateur spécifié (thread), mais cela ne signifie pas que les valeurs s'afficheront également sur ce thread.

Par exemple, si votre Observer ouvre une connexion réseau lorsque vous y êtes abonné, vous ne voulez pas que cela s'exécute sur le thread principal, par conséquent, vous avez besoin de subscribeOn pour spécifier où cet abonnement et donc la connexion réseau seront créés.

Lorsque les données arrivent enfin, le thread émetteur peut être n'importe quoi, l'un des planificateurs ou un ancien thread en arrière-plan. Puisque nous ne connaissons pas ou n'aimons pas ce fil, nous voulons déplacer l'observation des données vers un autre fil. C'est ce que fait observeOn: s'assure que les opérateurs après l'exécution de leur logique onNext sur le planificateur spécifié. Android les développeurs l'utilisent déjà pour déplacer l'observation des valeurs vers le fil principal.

Ce qui est rarement expliqué cependant, c'est ce qui se passe lorsque vous voulez un calcul supplémentaire sur le thread principal avant que le résultat final n'atteigne à nouveau le thread principal: utilisez plusieurs opérateurs observeOn:

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