J'apprends juste Rx-Java et Rxandroid2 et je suis juste confus quelle est la différence majeure entre dans SubscribeOn et ObserveOn.
S'abonnerSur spécifier le planificateur sur lequel un observable fonctionnera. ObserveOn spécifie le planificateur sur lequel un observateur observera cet observable.
Donc, fondamentalement, SubscribeOn est principalement souscrit (exécuté) sur un thread d'arrière-plan (vous ne voulez pas bloquer le thread d'interface utilisateur en attendant l'observable) et également dans ObserveOn, vous voulez observer le résultat sur un thread principal ...
Si vous connaissez AsyncTask, SubscribeOn est similaire à la méthode doInBackground et ObserveOn à onPostExecute ...
observeOn()
modifie simplement le thread de tous les opérateurs en aval . Les gens ont généralement cette idée fausse que observeOn
agit également comme en amont, mais ce n'est pas le cas.
L'exemple ci-dessous l'expliquera mieux ..
Observable.just("Some string") // UI
.map(str -> str.length()) // UI
.observeOn(Schedulers.computation()) // Changing the thread
.map(length -> 2 * length) // Computation
.subscribe(---)
subscribeOn()
seulement influence le thread qui va être utilisé quand Observable va s'abonner et il restera dessus en aval .
Observable.just("Some String") // Computation
.map(str -> str.length()) // Computation
.map(length -> 2 * length) // Computation
.subscribeOn(Schedulers.computation()) // -- changing the thread
.subscribe(number -> Log.d("", "Number " + number));// Computation
La position n'a pas d'importance (
subscribeOn()
)
Pourquoi? Parce que cela affecte seulement le moment de l'abonnement.
Méthodes obéissant au contact avec
subscribeOn
-> Exemple de base: Observable.create
Tout le travail spécifié dans le corps create
s'exécutera sur le thread spécifié dans subscribeOn
.
Un autre exemple: Observable.just
, Observable.from
Ou Observable.range
Remarque: Toutes ces méthodes acceptent des valeurs, donc n'utilisez pas de méthodes de blocage pour créer ces valeurs, car subscribeOn ne l'affectera pas.
Si vous souhaitez utiliser des fonctions de blocage, utilisez
Observable.defer(() -> Obervable.just(blockingMenthod())));
Fait important:
subscribeOn ne fonctionne pas avec Subjects
subscribeOn
multiples:
S'il y a plusieurs instances de subscribeOn
dans le flux, seule la première a un effet pratique.
Abonnez-vous &
subscribeOn
Les gens pensent que subscribeOn
a quelque chose à voir avec Observable.subscribe
, Mais cela n'a rien de spécial à voir avec ça. Cela n'affecte que la phase d'abonnement.
tl; dr Si rien de ce qui précède n'a de sens, regardez cet extrait de code
Observable.just("Some string")
.map(str -> str.length())
.observeOn(Schedulers.computation())
.map(length -> 2 * length)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(---)
Observez un observable, exécutez la fonction de carte sur le [~ # ~] ui [~ # ~] thread, passez maintenant à un Thread de calcul et effectuez la fonction
map(length -> 2 * length)
maintenant assurez-vous d'observer la sortie sur Main thread mais effectuer toutes les tâches définies soussubscribe()
dans un [~ # ~] io [~ # ~] thread.
Source: Tomek Polański ( moyen )
observeOn
pour définir des threads pour les rappels "plus loin dans le flux (en dessous)", tels que les blocs de code à l'intérieur de doOnNext
ou map
.subscribeOn
pour définir des unités d'exécution pour initialisations "en amont (au-dessus)", telles que doOnSubscribe
, Observable.just
Ou Observable.create
.Parcourons cette rubrique avec un exemple: nous voulons trouver la longueur de la chaîne "user1032613". Ce n'est pas une tâche facile pour les ordinateurs, il est donc naturel que nous effectuions le calcul intense dans un thread d'arrière-plan, pour éviter de geler l'application.
Nous pouvons appeler observeOn
autant de fois que nous le voulons, et il contrôle quel thread les rappels en dessous seront exécutés. Il est facile à utiliser et fonctionne exactement comme prévu.
Par exemple, nous allons afficher une barre de progression sur le thread d'interface utilisateur principal, puis effectuer des opérations intensives/de blocage dans un autre thread, puis revenir au thread d'interface utilisateur principal pour mettre à jour le résultat:
Observable.just("user1032613")
.observeOn(mainThread) // set thread for operation 1
.doOnNext {
/* operation 1 */
print("display progress bar")
progressBar.visibility = View.VISIBLE
}
.observeOn(backThread) // set thread for operation 2 and 3
.map {
/* operation 2 */
print("calculating")
Thread.sleep(5000)
it.length
}
.doOnNext {
/* operation 3 */
print("finished calculating")
}
.observeOn(mainThread) // set thread for operation 4
.doOnNext {
/* operation 4 */
print("hide progress bar and display result")
progressBar.visibility = View.GONE
resultTextView.text = "There're $it characters!"
}
.subscribe()
Dans l'exemple ci-dessus, /* operation 1 */
Est exécuté dans le mainThread
parce que nous le définissons en utilisant observeOn(mainThread)
sur la ligne juste au-dessus; nous passons ensuite à backThread
en appelant à nouveau observeOn
, afin que /* operation 2 */
y soit exécuté. Étant donné que nous ne l'avons pas modifié avant de chaîner /* operation 3 */
, Il s'exécutera également dans le thread arrière, tout comme /* operation 2 */
; enfin, nous appelons à nouveau observeOn(mainThread)
, pour nous assurer que /* operation 4 */
met à jour l'interface utilisateur à partir du thread principal.
Nous avons donc appris que observeOn
définit des threads pour les rappels ultérieurs. Que nous manque-t-il d'autre? Eh bien, le Observable
lui-même, et ses méthodes telles que just()
, create()
, subscribe()
et ainsi de suite, sont également du code qui doit être réalisé. C'est ainsi que les objets passent le long du flux. Nous utilisons subscribeOn
pour définir des threads pour le code lié à Observable
lui-même.
Si nous supprimons tous les rappels (contrôlés par observeOn
discutés précédemment), nous nous retrouvons avec le "code squelette" qui, par défaut, s'exécutera sur le thread dans lequel le code est écrit (probablement le thread principal):
Observable.just("user1032613")
.observeOn(mainThread)
.doOnNext {
}
.observeOn(backThread)
.map {
}
.doOnNext {
}
.observeOn(mainThread)
.doOnNext {
}
.subscribe()
Si nous ne sommes pas satisfaits de ce code squelette vide exécuté sur le thread principal, nous pouvons utiliser subscribeOn
pour le changer. Par exemple, peut-être que la première ligne Observable.just("user1032613")
n'est pas aussi simple que de créer un flux à partir de mon nom d'utilisateur - peut-être que c'est une chaîne provenant d'Internet, ou peut-être que vous utilisez doOnSubscribe
pour une autre opérations intensives. Dans ce cas, vous pouvez appeler subscribeOn(backThread)
pour mettre une partie du code dans un autre thread.
subscribeOn
Au moment d'écrire cette réponse, il y a certaines idées fausses qui disent "ne l'appeler qu'une seule fois", "la position n'a pas d'importance" et "si vous l'appelez plusieurs fois, seule la première fois compte". Après de nombreuses recherches et expériences, il s'avère que subscribeOn
peut être utilement appelé plusieurs fois.
Parce que Observable
utilise Builder Pattern (nom de fantaisie pour "les méthodes de chaînage les unes après les autres"), subscribeOn
est appliqué dans l'ordre inverse. Par conséquent, cette méthode définit le thread pour le code au-dessus , exactement l'opposé de observeOn
.
Nous pouvons expérimenter cela en utilisant la méthode doOnSubscribe
. Cette méthode est déclenchée sur l'événement d'abonnement et s'exécute sur le thread défini par subscribeOn
:
Observable.just("user1032613")
.doOnSubscribe {
print("#3 running on main thread")
}
.subscribeOn(mainThread) // set thread for #3 and just()
.doOnNext {
}
.map {
}
.doOnSubscribe {
print("#2 running on back thread")
}
.doOnNext {
}
.subscribeOn(backThread) // set thread for #2 above
.doOnNext {
}
.doOnSubscribe {
print("#1 running on default thread")
}
.subscribe()
Il pourrait être plus facile de suivre la logique, si vous lisez l'exemple ci-dessus de bas en haut, tout comme la façon dont Builder Pattern exécute le code.
Dans cet exemple, la première ligne Observable.just("user1032613")
est exécutée dans le même thread que print("#3")
car il n'y a plus de subscribeOn
entre eux. Cela crée l'illusion que "seul le premier appel compte" pour les personnes qui ne se soucient que du code à l'intérieur de just()
ou create()
. Ceci s'effondre rapidement une fois que vous commencez à en faire plus .
Les threads et les fonctions print()
dans les exemples sont définis, par souci de concision, comme suit:
val mainThread = AndroidSchedulers.mainThread()
val backThread = Schedulers.computation()
private fun print(msg: String) = Log.i("", "${Thread.currentThread().name}: $msg")
Si quelqu'un trouve rx Java description difficile à comprendre (comme moi par exemple), voici pure Java explication:
Observable.just("something")
.subscribeOn(Schedulers.newThread())
.subscribe(...);
Est équivalent à:
Observable observable = Observable.just("something");
new Thread(() -> observable.subscribe(...)).start();
Parce que Observable
émet des valeurs sur subscribe()
et ici subscribe()
va dans le thread séparé, les valeurs sont également émises dans le même thread que subscribe()
. C'est pourquoi cela fonctionne "en amont" (influence le thread pour les opérations précédentes) et "en aval".
Observable.just("something")
.observeOn(Schedulers.newThread())
.subscribe(...);
Est équivalent à:
Observable observable = Observable.just("something")
.subscribe(it -> new Thread(() -> ...).start());
Ici Observable
émet des valeurs dans le thread principal, seule la méthode d'écoute est exécutée dans le thread séparé.