Implementando operadores personalizados no RxJava

Implementando operadores personalizados no RxJava

1. Visão geral

Neste tutorial rápido, mostraremos como escrever um operador personalizado usandoRxJava.

Discutiremos como construir este operador simples, bem como um transformador - tanto como uma classe ou como uma função simples.

2. Configuração do Maven

Primeiro, precisamos ter certeza de que temos a dependênciarxjava empom.xml:


    io.reactivex
    rxjava
    1.3.0

Podemos verificar a versão mais recente derxjava emMaven Central.

3. Operador Customizado

We can create our custom operator by implementing Operator interface, no exemplo a seguir, implementamos um operador simples para remover caracteres não alfanuméricos de umString:

public class ToCleanString implements Operator {

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

    private ToCleanString() {
        super();
    }

    @Override
    public Subscriber call(final Subscriber subscriber) {
        return new Subscriber(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);
                }
            }
        };
    }
}

No exemplo acima, precisamos verificar se o assinante está inscrito antes de aplicar nossa operação e emitir o item, pois será desnecessário.

We are also restricting instance creation only to static factory methods to achieve a more user-friendly readability ao encadear métodos e usar a importação estática.

E agora, podemos usar o operadorlift para encadear nosso operador personalizado facilmente com outros operadores:

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

Aqui está um teste simples do nosso operador personalizado:

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

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

4. Transformador

Também podemos criar nosso operador implementando a interfaceTransformer:

public class ToLength implements Transformer {

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

    private ToLength() {
        super();
    }

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

Observe que usamos o transformadortoLength para transformar nosso observável deString para seu comprimento emInteger.

Precisaremos de um operadorcompose para usar nosso transformador:

observable.compose(toLength())...

Aqui está um teste simples:

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

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

Olift(Operator) opera nos assinantes do observável, mascompose(Transformer) trabalha no próprio observável.

Quando criamos nosso operador personalizado, devemos escolherTransformer se quisermos operar no observável como um todo e escolherOperator se quisermos operar nos itens emitidos pelo observável

5. Operador personalizado como função

Podemos implementar nosso operador personalizado como uma função em vez depublic class:

Operator cleanStringFn = subscriber -> {
    return new Subscriber(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);
            }
        }
    };
};

E aqui está o teste simples:

List results = new ArrayList<>();
Observable.from(Arrays.asList("[email protected]", "or-an?ge"))
  .lift(cleanStringFn)
  .subscribe(results::add);

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

Da mesma forma para o exemploTransformer:

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

    List 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. Conclusão

Neste artigo, mostramos como escrever nossos operadores RxJava.

E, como sempre, o código-fonte completo pode ser encontradoover on GitHub.