Я просто изучаю Rx-java и Rxandroid2, и я просто смущен тем, что является основным различием между SubscribeOn и ObserveOn.
Rxandroid Какая разница между SubscribeOn и ObserveOn
Ответ 1
SubscribeOn указать Планировщик, на котором будет работать Observable. ObserveOn определяет Планировщика, на котором наблюдатель будет наблюдать это Наблюдаемое.
Таким образом, в основном SubscribeOn в основном подписывается (выполняется) на фоновом потоке (вы не хотите блокировать поток пользовательского интерфейса в ожидании наблюдаемого), а также в ObserveOn вы хотите наблюдать результат по основному потоку...
Если вы знакомы с AsyncTask, тогда ScriptOn похож на метод doInBackground и ObserveOn на onPostExecute...
Ответ 2
observeOn()
просто изменяет поток всех операторов далее по течению. Люди обычно имеют такое заблуждение, что observeOn
также действует как восходящий observeOn
, но это не так.
Пример ниже объяснит это лучше..
Observable.just("Some string") // UI
.map(str -> str.length()) // UI
.observeOn(Schedulers.computation()) // Changing the thread
.map(length -> 2 * length) // Computation
.subscribe(---)
subscribeOn()
влияет только на поток, который будет использоваться, когда Observable будет подписан, и он останется в нисходящем направлении.
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
Позиция не имеет значения (
subscribeOn()
)
Зачем? Потому что это влияет только на время подписки.
Методы, которые подчиняются контакту с
subscribeOn
→ Основной пример: Observable.create
Вся работа, указанная внутри тела create
будет выполняться в потоке, указанном в subscribeOn
.
Другой пример: Observable.just
, Observable.from
или Observable.range
Примечание. Все эти методы принимают значения, поэтому не используйте методы блокировки для создания этих значений, так как subscribeOn не повлияет на это.
Если вы хотите использовать функции блокировки, используйте
Observable.defer(() → Obervable.just(blockingMenthod())));
Важный факт:
подписка не работает с Subjects
Многократная
subscribeOn
:
Если в потоке есть несколько экземпляров subscribeOn
, только первый имеет практический эффект.
Подписаться и
subscribeOn
Люди думают, что subscribeOn
имеет какое-то отношение к Observable.subscribe
, но не имеет к этому никакого отношения. Это влияет только на фазу подписки.
tl; dr. Если ничего из вышеперечисленного не имеет смысла, посмотрите на этот фрагмент кода.
Observable.just("Some string")
.map(str -> str.length())
.observeOn(Schedulers.computation())
.map(length -> 2 * length)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(---)
Заметим, наблюдаемой, выполняют функцию карты в потоке пользовательского интерфейса, теперь перейти к вычислению тему и выполнить
map(length → 2 * length)
функция Теперь убедитесь, что вы соблюдаете выход на основной поток, но выполнять все задачи, определенные в рамкахsubscribe()
в потоке ввода-вывода.
Источник: Томек Полански (Средний)
Ответ 3
Резюме
- Используйте
observeOn
чтобы установить потоки для обратных вызовов "дальше по потоку (под ним)", таких как блоки кода внутриdoOnNext
илиmap
. - Используйте
subscribeOn
чтобы установить потоки для инициализаций "вверх по течению (над ним)", такие какdoOnSubscribe
,Observable.just
илиObservable.create
. - Оба метода могут вызываться несколько раз, при этом каждый вызов перезаписывает предыдущие. Положение имеет значение.
Давайте пройдемся по этой теме на примере: мы хотим найти длину строки "user1032613". Это не простая задача для компьютеров, поэтому вполне естественно, что мы выполняем интенсивные вычисления в фоновом потоке, чтобы избежать зависания приложения.
observeOn
Мы можем вызывать observeOn
столько раз, сколько захотим, и он контролирует, в каком потоке будут выполняться все обратные вызовы ниже. Он прост в использовании и работает так, как вы ожидаете.
Например, мы покажем индикатор выполнения в основном потоке пользовательского интерфейса, затем выполним интенсивные/блокирующие операции в другом потоке, а затем вернемся к основному потоку пользовательского интерфейса, чтобы обновить результат:
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()
В приведенном выше примере /* operation 1 */
выполняется в mainThread
потому что мы установили его, используя observeOn(mainThread)
в строке прямо над ней; затем мы переключаемся на backThread
, снова вызывая observeOn
, поэтому /* operation 2 */
будет выполняться там. Поскольку мы не изменили его до создания цепочки /* operation 3 */
, он также будет выполняться в обратном потоке, как и /* operation 2 */
; наконец, мы observeOn(mainThread)
вызываем observeOn(mainThread)
, чтобы убедиться, что /* operation 4 */
обновляет пользовательский интерфейс из основного потока.
subscribeOn
Итак, мы научились observeOn
устанавливает потоки для последующих обратных вызовов. Что еще нам не хватает? Ну, сам Observable
и его методы, такие как just()
, create()
, subscribe()
и т.д., Также являются кодом, который необходимо выполнить. Вот как объекты передаются по потоку. Мы используем subscribeOn
для установки потоков для кода, связанного с самой Observable
.
Если мы удалим все обратные вызовы (управляемые ранее, как было observeOn
ранее, observeOn
), у нас останется "скелетный код", который по умолчанию будет выполняться в том или ином потоке, в котором написан код (возможно, в основном потоке):
Observable.just("user1032613")
.observeOn(mainThread)
.doOnNext {
}
.observeOn(backThread)
.map {
}
.doOnNext {
}
.observeOn(mainThread)
.doOnNext {
}
.subscribe()
Если мы не в восторге от этого пустой скелет кода работает на главном потоке, мы можем использовать subscribeOn
, чтобы изменить его. Например, возможно, первая строка Observable.just("user1032613")
не так проста, как создание потока из моего имени пользователя - возможно, это строка из Интернета, или, возможно, вы используете doOnSubscribe
для некоторых других интенсивных операций. В этом случае вы можете вызвать subscribeOn(backThread)
чтобы поместить часть кода в другой поток.
Где поставить subscribeOn
Во время написания этого ответа существуют некоторые неправильные представления о том, что "звоните только один раз", "позиция не имеет значения" и "если вы вызываете его несколько раз, считается только первый раз". После многих исследований и экспериментов subscribeOn
может быть вызвана несколько раз.
Поскольку Observable
использует шаблон Builder (причудливое имя для "методов цепочки один за другим"), subscribeOn
применяется в обратном порядке. Таким образом, этот метод устанавливает поток для кода над ним, в точности противоположный observeOn
.
Мы можем экспериментировать с doOnSubscribe
метода doOnSubscribe
. Этот метод запускается в событии подписки, и он запускается в потоке, установленном 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()
Возможно, будет проще следовать логике, если вы прочитаете приведенный выше пример снизу вверх, точно так же, как шаблон Builder выполняет код.
В этом примере первая строка Observable.just("user1032613")
запускается в том же потоке, что и print("#3")
потому что между ними больше нет subscribeOn
. Это создает иллюзию "важен только первый вызов" для людей, которым важен только код внутри just()
или create()
. Это быстро разваливается, как только вы начинаете делать больше.
Сноска:
Threads и функции print()
в примерах для краткости определены следующим образом:
val mainThread = AndroidSchedulers.mainThread()
val backThread = Schedulers.computation()
private fun print(msg: String) = Log.i("", "${Thread.currentThread().name}: $msg")
Ответ 4
Если кто-то находит описание rx java трудным для понимания (как я, например), вот чистое объяснение java:
subscribeOn()
Observable.just("something")
.subscribeOn(Schedulers.newThread())
.subscribe(...);
Это эквивалентно:
Observable observable = Observable.just("something");
new Thread(() -> observable.subscribe(...)).start();
Поскольку Observable
испускает значения в subscribe()
а здесь subscribe()
идет в отдельном потоке, значения также передаются в том же потоке, что и subscribe()
. Вот почему он работает "вверх по течению" (влияет на поток для предыдущих операций) и "вниз по течению".
observeOn()
Observable.just("something")
.observeOn(Schedulers.newThread())
.subscribe(...);
Это эквивалентно:
Observable observable = Observable.just("something")
.subscribe(it -> new Thread(() -> ...).start());
Здесь Observable
выдает значения в основном потоке, только метод слушателя выполняется в отдельном потоке.