В чем разница между операторами observeOn и subscribeOn в RxJava
Что проверяют
Понимаете ли вы, на каком потоке выполняется каждый кусок Rx-цепочки, а не просто помните шаблон subscribeOn(io) плюс observeOn(mainThread). Почти всегда следом спрашивают: что будет, если поставить два subscribeOn подряд, и где выполнится doOnNext между двумя observeOn.
Как отвечать
subscribeOn: задаёт поток, на котором подписка поднимается вверх к источнику. Для холодного источника вродеSingle.fromCallable { api.load() }это значит, что сама работа пойдёт на этом планировщике. Место в цепочке не важно, оператор влияет на всё выше по течению;observeOn: переключает поток для всего, что ниже по цепочке. Внутри очередь: элементы кладутся на одном потоке и забираются на другом. Можно ставить несколько раз, каждый меняет поток для следующего участка;- несколько
subscribeOn: источник выполнится на том, что ближе к нему. Остальные влияют только на участок, по которому проходит подписка, например наdoOnSubscribe; - горячие источники:
subscribeOnне поможетPublishSubject, он эмитит на том потоке, который вызвалonNext; - операторы со своим планировщиком:
interval,timer,delay,debounceпо умолчанию уходят наSchedulers.computation().
Типичная цепочка: subscribeOn(Schedulers.io()) для сети и базы, тяжёлый парсинг в map после observeOn(Schedulers.computation()), затем observeOn(AndroidSchedulers.mainThread()) перед обновлением UI. io() это растущий пул под блокирующие операции, computation() ограничен числом ядер. Подписку кладите в CompositeDisposable и очищайте в onCleared() у ViewModel, иначе будет утечка.
В новых проектах чаще встречается Kotlin Flow: там похожую роль играет flowOn, он меняет контекст для всего выше него.
Где спотыкаются
Говорят, что subscribeOn отвечает за поток «подписчика». Всё наоборот: подписчик получает данные там, где их выдал последний observeOn.
Ставят observeOn(AndroidSchedulers.mainThread()) в начало цепочки и потом делают тяжёлый map, блокируя UI.
Не замечают, что delay или debounce без явного планировщика увели выполнение с главного потока, и ловят CalledFromWrongThreadException при обновлении вьюхи.