Implémentation d’opérateurs personnalisés dans RxJava

Implémentation d'opérateurs personnalisés dans RxJava

1. Vue d'ensemble

Dans ce rapide tutoriel, nous allons montrer comment écrire un opérateur personnalisé à l'aide deRxJava.

Nous verrons comment créer cet opérateur simple, ainsi qu'un transformateur, à la fois en tant que classe ou en tant que fonction simple.

2. Configuration Maven

Tout d'abord, nous devons nous assurer que nous avons la dépendancerxjava danspom.xml:


    io.reactivex
    rxjava
    1.3.0

Nous pouvons vérifier la dernière version derxjava surMaven Central.

3. Opérateur personnalisé

We can create our custom operator by implementing Operator interface, dans l'exemple suivant, nous avons implémenté un opérateur simple pour supprimer les caractères non alphanumériques d'unString:

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);
                }
            }
        };
    }
}

Dans l'exemple ci-dessus, nous devons vérifier si l'abonné est abonné avant d'appliquer notre opération et de lui émettre l'élément, car il sera inutile.

We are also restricting instance creation only to static factory methods to achieve a more user-friendly readability lors du chaînage de méthodes et de l'utilisation de l'importation statique.

Et maintenant, nous pouvons utiliser l'opérateurlift pour enchaîner facilement notre opérateur personnalisé avec d'autres opérateurs:

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

Voici un test simple de notre opérateur personnalisé:

@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. Transformateur

Nous pouvons également créer notre opérateur en implémentant l'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);
    }
}

Notez que nous utilisons le transformateurtoLength pour transformer notre observable deString en sa longueur enInteger.

Nous aurons besoin d'un opérateurcompose pour utiliser notre transformateur:

observable.compose(toLength())...

Voici un test simple:

@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));
}

Lelift(Operator) fonctionne sur les abonnés de l'observable maiscompose(Transformer) travaille sur l'observable lui-même.

Lorsque nous créons notre opérateur personnalisé, nous devons choisirTransformer si nous voulons opérer sur l'observable dans son ensemble et choisirOperator si nous voulons opérer sur les éléments émis par l'observable

5. Opérateur personnalisé en tant que fonction

Nous pouvons implémenter notre opérateur personnalisé en tant que fonction au lieu 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);
            }
        }
    };
};

Et voici le test simple:

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"));

De même pour l'exempleTransformer:

@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. Conclusion

Dans cet article, nous avons montré comment écrire nos opérateurs RxJava.

Et, comme toujours, le code source complet peut être trouvéover on GitHub.