Rxandroid Какая разница между SubscribeOn и ObserveOn

Я просто изучаю Rx-java и Rxandroid2, и я просто смущен тем, что является основным различием между 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 выдает значения в основном потоке, только метод слушателя выполняется в отдельном потоке.