Перейти к основному содержимому

Реализация пользовательских операторов в RxJava

· 4 мин. чтения

1. Обзор

В этом кратком руководстве мы покажем, как написать собственный оператор с помощью RxJava .

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

2. Конфигурация Maven

Во-первых, нам нужно убедиться, что у нас есть зависимость rxjava в pom.xml :

<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<version>1.3.0</version>
</dependency>

Мы можем проверить последнюю версию rxjava на Maven Central .

3. Пользовательский оператор

Мы можем создать наш собственный оператор, реализовав интерфейс оператора . В следующем примере мы реализовали простой оператор для удаления не буквенно-цифровых символов из строки :

public class ToCleanString implements Operator<String, String> {

public static ToCleanString toCleanString() {
return new ToCleanString();
}

private ToCleanString() {
super();
}

@Override
public Subscriber<? super String> call(final Subscriber<? super String> subscriber) {
return new Subscriber<String>(subscriber) {
@Override
public void onCompleted() {
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
}

@Override
public void onError(Throwable t) {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(t);
}
}

@Override
public void onNext(String item) {
if (!subscriber.isUnsubscribed()) {
final String result = item.replaceAll("[^A-Za-z0-9]", "");
subscriber.onNext(result);
}
}
};
}
}

В приведенном выше примере нам нужно проверить, подписан ли подписчик, прежде чем применять нашу операцию и передавать ему элемент, поскольку в этом нет необходимости.

Мы также ограничиваем создание экземпляров только статическими фабричными методами, чтобы обеспечить более удобную для пользователя читаемость при цепочке методов и использовании статического импорта.

И теперь мы можем использовать оператор лифта , чтобы легко связать наш пользовательский оператор с другими операторами:

observable.lift(toCleanString())....

Вот простой тест нашего пользовательского оператора:

@Test
public void whenUseCleanStringOperator_thenSuccess() {
List<String> list = Arrays.asList("john_1", "tom-3");
List<String> results = new ArrayList<>();
Observable<String> observable = Observable
.from(list)
.lift(toCleanString());
observable.subscribe(results::add);

assertThat(results, notNullValue());
assertThat(results, hasSize(2));
assertThat(results, hasItems("john1", "tom3"));
}

4. Трансформатор

Мы также можем создать нашего оператора, реализовав интерфейс Transformer :

public class ToLength implements Transformer<String, Integer> {

public static ToLength toLength() {
return new ToLength();
}

private ToLength() {
super();
}

@Override
public Observable<Integer> call(Observable<String> source) {
return source.map(String::length);
}
}

Обратите внимание, что мы используем преобразователь toLength для преобразования нашего наблюдаемого из String в его длину в Integer .

Нам понадобится оператор compose для использования нашего преобразователя:

observable.compose(toLength())...

Вот простой тест:

@Test
public void whenUseToLengthOperator_thenSuccess() {
List<String> list = Arrays.asList("john", "tom");
List<Integer> results = new ArrayList<>();
Observable<Integer> observable = Observable
.from(list)
.compose(toLength());
observable.subscribe(results::add);

assertThat(results, notNullValue());
assertThat(results, hasSize(2));
assertThat(results, hasItems(4, 3));
}

Лифт (оператор) работает с наблюдаемыми подписчиками, но компоновщик (преобразователь) работает с самим наблюдаемым.

Когда мы создаем наш собственный оператор, мы должны выбрать Transformer , если мы хотим работать с наблюдаемым в целом, и выбрать Operator , если мы хотим работать с элементами, испускаемыми наблюдаемым.

5. Пользовательский оператор как функция

Мы можем реализовать наш пользовательский оператор как функцию вместо публичного класса :

Operator<String, String> cleanStringFn = subscriber -> {
return new Subscriber<String>(subscriber) {
@Override
public void onCompleted() {
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
}

@Override
public void onError(Throwable t) {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(t);
}
}

@Override
public void onNext(String str) {
if (!subscriber.isUnsubscribed()) {
String result = str.replaceAll("[^A-Za-z0-9]", "");
subscriber.onNext(result);
}
}
};
};

А вот и простой тест:

List<String> results = new ArrayList<>();
Observable.from(Arrays.asList("ap_p-l@e", "or-an?ge"))
.lift(cleanStringFn)
.subscribe(results::add);

assertThat(results, notNullValue());
assertThat(results, hasSize(2));
assertThat(results, hasItems("apple", "orange"));

Аналогично для примера с трансформатором :

@Test
public void whenUseFunctionTransformer_thenSuccess() {
Transformer<String, Integer> toLengthFn = s -> s.map(String::length);

List<Integer> results = new ArrayList<>();
Observable.from(Arrays.asList("apple", "orange"))
.compose(toLengthFn)
.subscribe(results::add);

assertThat(results, notNullValue());
assertThat(results, hasSize(2));
assertThat(results, hasItems(5, 6));
}

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

В этой статье мы показали, как писать наши операторы RxJava.

И, как всегда, полный исходный код можно найти на GitHub .