Преобразование синхронных и асинхронных API в Observables с помощью RxJava2

Преобразование синхронных и асинхронных API в Observables с использованием RxJava2

1. обзор

В этом руководстве мы изучим операторыhow to transform traditional synchronous and asynchronous APIs into Observables using RxJava2.

Мы создадим несколько простых функций, которые помогут нам подробно обсудить эти операторы.

2. Maven Зависимости

Во-первых, мы должны добавитьRxJava2 иRxJava2Extensions как зависимости Maven:


    io.reactivex.rxjava2
    rxjava
    2.2.2


    com.github.akarnokd
    rxjava2-extensions
    0.20.4

3. Операторы

RxJava2 определяетa whole lot of operators для различных вариантов использования реактивного программирования.

Но мы обсудим только несколько операторов, которые обычно используются для преобразования синхронных или асинхронных методов вObservables в зависимости от их природы. These operators take functions as arguments and emit the value returned from that function.

Наряду с обычными операторами, RxJava2 определяет еще несколько операторов для расширенных функций.

Давайте посмотрим, как мы можем использовать эти операторы для преобразования синхронных и асинхронных методов.

4. Синхронное преобразование метода

4.1. ИспользуяfromCallable()

Этот оператор возвращаетObservable, которое, когда подписчик подписывается на него, вызывает функцию, переданную в качестве аргумента, а затем выдает значение, возвращаемое этой функцией. Давайте создадим функцию, которая возвращает целое число, и преобразуем его:

AtomicInteger counter = new AtomicInteger();
Callable callable = () -> counter.incrementAndGet();

Теперь давайте преобразуем его вObservable и протестируем, подписавшись на него:

Observable source = Observable.fromCallable(callable);

for (int i = 1; i < 5; i++) {
    source.test()
      .awaitDone(5, TimeUnit.SECONDS)
      .assertResult(i);
    assertEquals(i, counter.get());
}

The fromCallable() operator executes the specified function lazily each time when the wrapped Observable gets subscribed. Чтобы проверить это поведение, мы создали несколько подписчиков с помощью цикла.

Поскольку реактивные потоки по умолчанию асинхронны, подписчик сразу же вернется. В большинстве практических сценариев вызываемая функция будет иметь некоторую задержку для завершения своего выполнения. Итак,we’ve added a maximum wait time of five seconds перед тестированием результата нашей вызываемой функции.

Также обратите внимание, что мы использовали методObservable ’stest(). This method is handy when testing Observables. СоздаетTestObserver and, который подписывается на нашObservable.

4.2. Используяstart()

Операторstart() является частью модуляRxJava2Extension. Он вызовет указанную функцию асинхронно и вернетObservable, который выдает результат:

Observable source = AsyncObservable.start(callable);

for (int i = 1; i < 5; i++) {
    source.test()
      .awaitDone(5, TimeUnit.SECONDS)
      .assertResult(1);
    assertEquals(1, counter.get());
}

Функция вызывается немедленно, а не всякий раз, когда подписчик подписывается на результирующийObservable. Multiple subscriptions to this observable observe the same return value.

5. Преобразование асинхронного метода

5.1. ИспользуяfromFuture()

Как мы знаем, наиболее распространенный способ создания асинхронного метода в Java - это реализацияFuture. МетодfromFuture принимает в качестве аргументаFuture и выдает значение, полученное из методаFuture.get().

Во-первых, давайте сделаем функцию, которую мы создали ранее, асинхронной:

ExecutorService executor = Executors.newSingleThreadExecutor();
Future future = executor.submit(callable);

Теперь давайте проведем тестирование, изменив его:

Observable source = Observable.fromFuture(future);

for (int i = 1; i < 5; i++) {
    source.test()
      .awaitDone(5, TimeUnit.SECONDS)
      .assertResult(1);
    assertEquals(1, counter.get());
}
executor.shutdown();

И еще раз обратите внимание, что каждая подписка имеет одно и то же возвращаемое значение.

Теперь методdispose() дляObservable действительно полезен, когда дело доходит до предотвращения утечки памяти. Но в этом случае он не отменит будущее из-за блокирующего характераFuture.get().

Итак, мы можем убедиться, что отменили будущее с помощью функцииcombining the doOnDispose() наблюдаемогоsource и методаcancel дляfuture:

source.doOnDispose(() -> future.cancel(true));

5.2. ИспользуяstartFuture()

Как видно из названия, этот оператор немедленно запускает указанныйFuture и выдает возвращаемое значение, когда подписчик подписывается на него. В отличие от оператораfromFuture, который кэширует результат для следующего использования,this operator will execute the asynchronous method each time when it gets subscribed:

ExecutorService executor = Executors.newSingleThreadExecutor();
Observable source = AsyncObservable.startFuture(() -> executor.submit(callable));

for (int i = 1; i < 5; i++) {
    source.test()
      .awaitDone(5, TimeUnit.SECONDS)
      .assertResult(i);
    assertEquals(i, counter.get());
}
executor.shutdown();

5.3. ИспользуяdeferFuture()

This operator aggregates multiple Observables returned from a Future method and returns a stream of return values obtained from each Observable. Это запустит переданную асинхронную фабричную функцию всякий раз, когда новый подписчик подписывается.

Итак, давайте сначала создадим асинхронную фабричную функцию:

List list = Arrays.asList(new Integer[] { counter.incrementAndGet(),
  counter.incrementAndGet(), counter.incrementAndGet() });
ExecutorService exec = Executors.newSingleThreadExecutor();
Callable> callable = () -> Observable.fromIterable(list);

И тогда мы можем сделать быстрый тест:

Observable source = AsyncObservable.deferFuture(() -> exec.submit(callable));
for (int i = 1; i < 4; i++) {
    source.test()
      .awaitDone(5, TimeUnit.SECONDS)
      .assertResult(1,2,3);
}
exec.shutdown();

6. Заключение

В этом руководстве мы узнали, как преобразовать синхронные и асинхронные методы в наблюдаемые RxJava2.

Конечно, примеры, которые мы здесь показали, являются основными реализациями. Но мы можем использовать RxJava2 для более сложных приложений, таких как потоковое видео и приложений, где нам нужно отправлять большие объемы данных порциями.

Как обычно, все краткие примеры, которые мы здесь обсуждали, можно найти наGithub project.