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 .