RxJava — це бібліотека реактивного програмування для JVM, яка реалізує асинхронні потоки даних через патерн Observable з функціональними операторами трансформації. Вона портує концепції ReactiveX на Java та Kotlin, надаючи єдиний API для роботи з мережевими запитами, базами даних, UI-подіями та фоновими завданнями. За даними ReactiveX, 2025, бібліотека використовується більш ніж у 120 000 проектах на GitHub і є стандартом реактивного програмування для Android до появи Kotlin Flow. RxJava замінює AsyncTask, Loader та callback-и єдиним ланцюжком обробки даних.
Головне
RxJava — це реалізація бібліотеки ReactiveX (Reactive Extensions) для віртуальної машини Java. Перша версія RxJava була випущена компанією Netflix у 2013 році для управління асинхронними викликами в серверних додатках. На момент створення основною альтернативою в Java були Future та Callback — обидва підходи призводили до callback-hell та складного управління потоками. RxJava запропонувала композицію асинхронних операцій через Observable з ланцюжками функціональних операторів.
Архітектура RxJava базується на специфікації Reactive Streams — стандарті для асинхронної обробки потоків з неблокуючим backpressure. Специфікація визначає чотири інтерфейси: Publisher, Subscriber, Subscription та Processor. RxJava 2+ повністю реалізує Reactive Streams через тип Flowable, дотримуючись контрактів backpressure на відміну від RxJava 1. Observable в RxJava 2 не підтримує backpressure — він призначений для потоків з невеликою кількістю подій або UI-подій.
Згідно з опитуванням JetBrains, 2025, RxJava входить до топ-3 бібліотек для Android-розробки. Основні сценарії використання: обробка мережевих запитів через Retrofit (інтегрований з RxJava через CallAdapter), робота з Room (реактивні запити повертають Flowable або Maybe), анімації та UI-події через RxBinding, та дебаунс-пошук при введенні тексту. Всі ці сценарії об'єднує однотипний ланцюжок: джерело (Observable) → трансформація (оператори) → підписка (subscribe).
RxJava 1 (2013) заклав концепцію Observable та операторів, але страждав від проблем із backpressure — у швидких потоках дані накопичувалися в пам'яті, викликаючи OutOfMemoryError. RxJava 2 (2016) виправив архітектуру, розділивши Observable (без backpressure) та Flowable (з backpressure). RxJava 3 (2020) додав підтримку Java 8 Stream API, додаткові оператори та покращену продуктивність підписки. На поточний момент RxJava 3 — рекомендована версія для нових проектів.
RxJava надає п'ять основних типів реактивних джерел, кожен з яких орієнтований на певний сценарій. Observable та Flowable випускають безліч значень, Single — одне значення або помилку, Completable — лише факт завершення без даних, Maybe — одне значення, нуль або помилку. Вибір правильного типу скорочує кількість коду та робить ланцюжок самодокументованим.
| Тип | Кількість подій | Backpressure | Сценарій |
|---|---|---|---|
| Observable | 0..N, потім завершення | Ні | UI-події, короткі потоки |
| Flowable | 0..N, потім завершення | Так | Мережеві відповіді, потоки з БД |
| Single | Рівно 1 або помилка | Ні | HTTP-запит, читання одного запису |
| Completable | 0 (тільки завершення) | Ні | Запис у БД, надсилання події |
| Maybe | 0, 1 або помилка | Ні | Кеш: значення є чи немає |
Flowable — найбільш гнучкий тип для роботи з великими потоками даних. Він реалізує Reactive Streams Publisher з підтримкою backpressure: споживач може запросити певну кількість елементів через Subscription.request(n). Це запобігає переповненню буфера при невідповідності швидкостей виробника та споживача. Якщо backpressure не критичний — використовуйте Observable, він має менше накладних витрат через відсутність механізму request.
Single — оптимальний вибір для HTTP-запитів. Retrofit 2 з RxJava CallAdapter повертає Single<ResponseBody> для кожного запиту. Single гарантує рівно один виклик onSuccess або onError, що відповідає семантиці HTTP-запиту — одна відповідь або одна помилка. Completable використовується для операцій запису, що не повертають даних: insert, update, delete. Maybe зручний при перевірці кешу — може повернути значення, а може не повернути.
// Приклад використання Single для HTTP-запиту
interface ApiService {
@GET("users/{id}")
fun getUser(@Path("id") userId: Int): Single<User>
}
// Підписка з обробкою на головному потоці
apiService.getUser(42)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ user ->
textView.text = user.name
}, { error ->
Log.e("API", "Error: ${error.message}")
})
.addTo(compositeDisposable)
Оператори RxJava — це функції вищого порядку, які приймають одне реактивне джерело та повертають інше, трансформуючи потік даних. RxJava 3 містить понад 400 операторів, розділених на категорії: трансформація, фільтрація, комбінування, обробка помилок та управління часом. Кожен оператор лінивий — ланцюжок будується при декларації, виконується при підписці.
map — базовий оператор, що перетворює кожне значення через функцію. flatMap приймає функцію, яка повертає Observable для кожного елемента, та розгортає результат в єдиний потік. switchMap схожий на flatMap, але при надходженні нового елемента відписується від попереднього Observable. concatMap зберігає порядок елементів — на відміну від flatMap, він послідовно підписується на кожен вкладений Observable.
// Парсинг JSON з трансформацією та фільтрацією
apiService.getUsers()
.flatMap { users ->
Observable.fromIterable(users)
}
.filter { user ->
user.age >= 18
}
.map { user ->
UserDto(user.name, user.age)
}
.toList()
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ adapter.submitList(it) },
{ Log.e("Помилка", it.message) })
Комбінування потоків — область, де RxJava особливо сильний. zip об'єднує елементи з кількох Observable попарно за індексом: перший з першим, другий з другим. combineLatest випускає нове значення при зміні будь-якого з потоків, комбінуючи останні значення всіх потоків. merge об'єднує кілька Observable в один, зберігаючи порядок надходження подій. concat послідовно підписується на кожен Observable та передає всі його події, перш ніж перейти до наступного.
Управління часом включає debounce (очікування паузи в потоці перед відправкою), throttleFirst (пропуск першої події, ігнорування інших протягом вікна), timeout (помилка, якщо подія не надійшла протягом інтервалу). Дебаунс-пошук при введенні тексту — найпоширеніший сценарій: searchObservable.debounce(300, MILLISECONDS).distinctUntilChanged() запобігає зайвим запитам при швидкому наборі.
| Категорія | Оператор | Поведінка |
|---|---|---|
| Трансформація | map / flatMap / switchMap | Перетворення одного значення або потоку |
| Фільтрація | filter / distinct / take | Відбір значень за умовою |
| Комбінування | zip / combineLatest / merge | Об'єднання 2+ потоків |
| Помилки | onErrorResumeNext / retry | Відновлення після збою |
| Утиліти | delay / timeout / debounce | Управління часом у потоці |
Scheduler у RxJava — це абстракція над пулом потоків. Бібліотека надає п'ять вбудованих Scheduler: Schedulers.io() для I/O-операцій (мережа, файли), Schedulers.computation() для CPU-інтенсивних завдань, Schedulers.newThread() для кожного нового потоку, Schedulers.single() для однопотокового виконання та Schedulers.trampoline() для негайного виконання в поточному потоці.
subscribeOn визначає, на якому Scheduler виконується джерело Observable. Якщо в ланцюжку кілька subscribeOn — пріоритет у найближчого до джерела. observeOn перемикає downstream на вказаний Scheduler — кожне використання observeOn змінює потік для наступних операторів. Типовий Android-патерн: subscribeOn(Schedulers.io()) для роботи з мережею, observeOn(AndroidSchedulers.mainThread()) для оновлення UI.
// Багатопотокова обробка з перемиканням контексту
Observable.fromCallable(() -> database.getItems())
.subscribeOn(Schedulers.io()) // БД на io
.map(items -> processItems(items)) // трансформація на io
.observeOn(Schedulers.computation()) // перемикаємо на computation
.map(processed -> compressImages(processed))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> ui.showResult(result))
AndroidSchedulers.mainThread() — Scheduler з бібліотеки RxAndroid, який виконує код на головному потоці Android. Він обов'язковий для будь-яких оновлень UI в реактивному ланцюжку. Бібліотека використовує Handler всередині та гарантує виконання в UI-потоці навіть при високому навантаженні. Для фонових операцій Schedulers.io() підтримує необмежений пул потоків та підходить для будь-яких блокуючих операцій. Schedulers.computation() використовує фіксований пул, рівний кількості ядер процесора.
RxJava в Android використовується для трьох основних сценаріїв: реактивні запити до Room, інтеграція з Retrofit та реактивне зв'язування UI через RxBinding. Для кожного сценарію характерний свій набір типів: Room повертає Flowable для спостережуваних запитів, Retrofit — Single для HTTP-запитів, RxBinding — Observable для UI-подій.
Room — бібліотека персистентності даних від Google. Починаючи з Room 2.1, база даних підтримує реактивні типи, що повертаються: Flowable та Observable. При зміні будь-якого запису в таблиці Room автоматично надсилає нове значення в потік. Розробник підписується на Flowable в ViewModel та отримує актуальні дані без ручних запитів при кожній зміні.
// Room DAO з реактивним запитом
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :id")
fun getUserById(@Param("id") userId: Int): Flowable<User>
@Insert
fun insertUser(user: User): Completable
}
// ViewModel — композиція Room + Network
class UserViewModel(private val dao: UserDao) : ViewModel() {
val users: Flowable<List<User>> = dao.getAllUsers()
.subscribeOn(Schedulers.io())
}
Патерн MVVM + RxJava будується на тому, що ViewModel не має посилань на View. ViewModel публікує реактивні джерела (Flowable, LiveData через Transformations), а Activity або Fragment підписуються на них. Це дає тестованість: ViewModel тестується без UI, підміняючи Scheduler через RxJavaPlugins.setComputationScheduler. CompositeDisposable у ViewModel керує життєвим циклом підписок — при onCleared() всі підписки скасовуються.
Kotlin Flow — нативна реалізація холодних потоків у Kotlin, вбудована в корутини та представлена в Kotlin 1.3. Flow вирішує ті самі завдання, що й RxJava, але з фундаментальними відмінностями: вбудована підтримка корутин (suspend-функції), скасування через coroutine cancellation та відсутність проблем із backpressure — Flow використовує suspend замість буферизації. Flow є частиною стандартної бібліотеки Kotlin, не вимагаючи додаткових залежностей.
RxJava залишається кращим вибором для проектів на Java, проектів з підтримкою Java 7-8 та існуючих кодових баз на RxJava. Екосистема RxJava значно багатша: >400 операторів проти ~50 у Flow, інтеграція з Retrofit через вбудований CallAdapter, підтримка backpressure через Flowable та наявність RxBinding, RxPermissions, RxLocation для Android. Kotlin Flow стрімко наздоганяє, але гнучкість RxJava в складних сценаріях комбінування потоків поки вища.
| Характеристика | RxJava | Kotlin Flow |
|---|---|---|
| Мова | Java / Kotlin | Kotlin тільки |
| Скасування | Disposable / CompositeDisposable | Coroutine cancellation |
| Backpressure | Flowable (стратегії BUFFER, DROP, LATEST) | Через conflate / buffer |
| Оператори | 400+ | ~50 (розширюється) |
| Room інтеграція | Flowable, Observable | Flow, StateFlow |
| ViewModel | CompositeDisposable | viewModelScope + Flow |
Часті запитання
Observable не підтримує backpressure — якщо виробник швидший за споживача, події накопичуються в пам'яті. Flowable реалізує Reactive Streams з backpressure через Subscription.request(), що запобігає переповненню буфера при невідповідності швидкостей.
Single використовується для операцій, які повертають рівно одне значення або помилку: HTTP-запити, читання одного запису з БД, обчислення результату. Single семантично відповідає Future та скорочує код, прибираючи невикористовуваний onComplete.
Метод dispose() на Disposable скасовує підписку. Для групового управління використовується CompositeDisposable — він збирає всі Disposable та скасовує їх одночасно при виклику clear(). Типове місце — onCleared() у ViewModel або onPause() в Activity.
flatMap підписується на всі вкладені Observable та об'єднує їх події в довільному порядку. switchMap при надходженні нового елемента відписується від попереднього Observable та підписується на новий. switchMap використовується при пошуку — кожен новий запит скасовує попередній.
Для нових проектів на Kotlin Flow кращий завдяки інтеграції з корутинами та меншому розміру. Для існуючих проектів на RxJava міграція виправдана лише якщо вся кодова база переходить на корутини — проміжне використання обох бібліотек ускладнює архітектуру.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.