web-dev-qa-db-fra.com

Création d'Observable sans utiliser Observable.create

J'utilise RxJava dans mon Android et je souhaite charger des données à partir de la base de données.

De cette façon, je crée un nouvel observable en utilisant Observable.create() qui retourne une liste de EventLog

public Observable<List<EventLog>> loadEventLogs() {
    return Observable.create(new Observable.OnSubscribe<List<EventLog>>() {
        @Override
        public void call(Subscriber<? super List<EventLog>> subscriber) {
            List<DBEventLog> logs = new Select().from(DBEventLog.class).execute();
            List<EventLog> eventLogs = new ArrayList<>(logs.size());
            for (int i = 0; i < logs.size(); i++) {
                eventLogs.add(new EventLog(logs.get(i)));
            }
            subscriber.onNext(eventLogs);
        }
    });
}

Bien que cela fonctionne correctement, j'ai lu que l'utilisation de Observable.create() n'est pas en fait une meilleure pratique pour Rx Java (voir ici ).

J'ai donc changé cette méthode de cette façon.

public Observable<List<EventLog>> loadEventLogs() {
    return Observable.fromCallable(new Func0<List<EventLog>>() {
        @Override
        public List<EventLog> call() {
            List<DBEventLog> logs = new Select().from(DBEventLog.class).execute();
            List<EventLog> eventLogs = new ArrayList<>(logs.size());
            for (int i = 0; i < logs.size(); i++) {
                eventLogs.add(new EventLog(logs.get(i)));
            }
            return eventLogs;
        }
    });
}

Est-ce une meilleure approche en utilisant Rx Java? Pourquoi? Quelle est réellement la différence entre les deux méthodes?

De plus, puisque la base de données charge une liste d'éléments, est-il judicieux d'émettre la liste entière à la fois? Ou dois-je émettre un élément à la fois?

32
Daniele Vitali

Les deux méthodes peuvent sembler similaires et se comporter de la même façon, mais fromCallable gère les difficultés de contre-pression pour vous, contrairement à la version create. La gestion de la contre-pression dans une implémentation de OnSubscribe va de la simple à la déconcertante; cependant, s'il est omis, vous pouvez obtenir MissingBackpressureExceptions le long des frontières asynchrones (telles que observeOn) ou même sur les frontières de continuation (telles que concat).

RxJava essaie d'offrir une prise en charge adéquate de la contre-pression pour autant d'usines et d'opérateurs que possible, cependant, il y a pas mal d'usines et d'opérateurs qui ne peuvent pas la prendre en charge.

Le deuxième problème avec l'implémentation manuelle de OnSubscribe est le manque de support d'annulation, surtout si vous générez beaucoup d'appels onNext. Beaucoup d'entre eux peuvent être remplacés par des méthodes d'usine standard (telles que from) ou des classes d'assistance (telles que SyncOnSubscribe) qui traitent de toute la complexité pour vous.

Vous pouvez trouver de nombreuses introductions et exemples qui utilisent (toujours) create pour deux raisons.

  1. Il est beaucoup plus facile d'introduire des flux de données basés sur Push en montrant comment le Push d'événements fonctionne de manière impérative. À mon avis, ces sources passent trop de temps avec create proportionnellement au lieu de parler des méthodes d'usine standard et de montrer comment certaines tâches courantes (comme la vôtre) peuvent être accomplies en toute sécurité.
  2. Beaucoup de ces exemples ont été créés lorsque RxJava ne nécessitait pas de prise en charge de la contre-pression ou même une prise en charge d'annulation synchrone appropriée ou étaient simplement portés à partir des exemples Rx.NET (qui à ce jour ne prennent pas en charge la contre-pression et l'annulation synchrone fonctionne d'une manière ou d'une autre, gracieuseté de C # I devinez.) La génération de valeurs en appelant onNext était alors sans souci. Cependant, une telle utilisation entraîne un gonflement de la mémoire tampon et une utilisation excessive de la mémoire.Par conséquent, l'équipe Netflix a trouvé un moyen de limiter l'utilisation de la mémoire en exigeant des observateurs qu'ils indiquent le nombre d'éléments qu'ils sont prêts à poursuivre. Ceci est devenu connu sous le nom de contre-pression.

Pour la deuxième question, à savoir si l'on doit créer une liste ou une séquence de valeurs, cela dépend de votre source. Si votre source prend en charge une sorte d'itération ou de streaming d'éléments de données individuels (tels que JDBC), vous pouvez simplement vous y connecter et émettre un par un (voir SyncOnSubscribe). S'il ne le prend pas en charge ou si vous en avez besoin sous forme de liste, conservez-le tel quel. Vous pouvez toujours convertir entre les deux formulaires via toList et flatMapIterable si nécessaire.

34
akarnokd

Comme il est expliqué dans la réponse vous avez lié, avec Observable.create vous devrez peut-être enfreindre les exigences avancées de RxJava.

Par exemple, vous devrez implémenter backpressure , ou comment vous désinscrire.

Dans votre cas, vous souhaitez émettre un élément, sans avoir à faire face à la contre-pression ou à l'abonnement. Alors Observable.fromCallable est un bon appel. RxJava s'occupera du reste.

2
dwursteisen