Grokai * RxJava, erster Teil: die Grundlagen

Ursprünglicher Autor: Dan Lew
  • Übersetzung
* von einem Übersetzer: Ich habe darüber nachgedacht, wie man das Verb „to grok“ ins Russische übersetzt. Einerseits bedeutet dieses Wort „verstehen“ oder „realisieren“, und andererseits haben die Übersetzer bei der Übersetzung von Robert Heinleins Roman „Ein Fremder in einem fremden Land“ (in dem dieses Wort zum ersten Mal geboren wurde) Russisch gemacht. brüllen. " Ich habe den Roman nicht gelesen, daher habe ich angenommen, dass dieses Wort einige semantische Nuancen aufweist, die von russischen Analoga nicht übermittelt wurden, und habe deshalb dasselbe Transparentpapier aus dem Englischen in seiner Übersetzung verwendet.

Rxjava- Dies ist jetzt eines der heißesten Themen für die Diskussion unter Android-Programmierern. Das einzige Problem ist, dass es ziemlich schwierig sein kann, die Grundlagen zu verstehen, wenn Ihnen so etwas noch nicht begegnet ist. Funktionale reaktive Programmierung ist ziemlich schwer zu verstehen, wenn Sie aus der imperativen Welt kommen, aber sobald Sie sich damit befassen, werden Sie feststellen, wie cool es ist!
Ich werde versuchen, Ihnen eine allgemeine Vorstellung von RxJava zu geben. Das Ziel dieser Artikelserie ist nicht, alles bis zum letzten Komma zu erklären (ich könnte das kaum tun), sondern RxJava und seine Funktionsweise zu interessieren.

Die Grundlagen


Die Grundbausteine des Codes ist reaktiv Observablesund Subscribers1 . ObservableEs ist eine Datenquelle, aber Subscriberein Verbraucher.
Die Generierung von Daten Observableerfolgt immer nach dem gleichen Verfahren: Sie Observable"sendet" eine bestimmte Menge von Daten aus (einschließlich, sie sendet Observablemöglicherweise nichts aus) und beendet ihre Arbeit entweder erfolgreich oder mit einem Fehler. Für jeden Subscribersigniert Observablewird die Methode aufgerufen Subscriber.onNext()für jedes Datenstrom - Element, wonach beide genannt werden können Subscriber.onComplete(), und so Subscriber.onError().
All dies ist dem üblichen "Beobachter" -Muster sehr ähnlich , aber es gibt einen wichtigen Unterschied:ObservablesBeginnen Sie oft erst dann mit der Datenerzeugung, wenn jemand sie ausdrücklich abonniert hat. 2 . Mit anderen Worten: Wenn ein Baum fällt, aber niemand in der Nähe ist, ist das Geräusch seines Falls nicht zu hören .

Hallo Welt!


Lassen Sie uns ein kleines Beispiel behandeln. Erstellen Sie zunächst eine einfache Observable:

Observable myObservable = Observable.create(
    new Observable.OnSubscribe() {
        @Override
        public void call(Subscriber sub) {
            sub.onNext("Hello, world!");
            sub.onCompleted();
        }
    }
);

Unserer Observablebringt den String "Hallo Welt!" Hervor und beendet seine Arbeit. Jetzt erstellen Subscriber, um Daten zu empfangen und etwas damit zu tun.

Subscriber mySubscriber = new Subscriber() {
    @Override
    public void onNext(String s) { System.out.println(s); }
    @Override
    public void onCompleted() { }
    @Override
    public void onError(Throwable e) { }
};

Es werden nur Subscriberdie übergebenen Zeilen gedruckt Observable. Nachdem wir myObservableund haben mySubscriber, können wir sie mit der folgenden Methode zusammenbinden subscribe():

myObservable.subscribe(mySubscriber);
// Выводит "Hello, world!"

Sobald wir uns dafür angemeldet mySubscriberhaben myObservable, myObservableruft es die mySubscriberMethoden auf onNext()und zeigt in der Konsole onComplete()als Ergebnis mySubscriber"Hallo Welt!" An und beendet die Ausführung.

Vereinfachen Sie den Code


Im Allgemeinen haben wir zu viel Code für eine so einfache Aufgabe wie die Ausgabe von „Hallo Welt!“ An die Konsole geschrieben. Ich habe diesen Code speziell geschrieben, damit Sie leicht herausfinden können, was was ist. RxJava bietet viele weitere optimierte Möglichkeiten, um dieses Problem zu lösen.
Vereinfachen wir zunächst unseren Observable. In RxJava gibt es Erstellungsmethoden Observable, mit denen die häufigsten Aufgaben gelöst werden können. In unserem Fall wird Observable.just()ein Datenelement erzeugt und anschließend wie in unserer ersten Option 3 ausgeführt :

Observable myObservable = Observable.just("Hello, world!");

Als nächstes vereinfachen wir unsere Subscriber. Wir interessieren uns nicht für Methoden onCompleted()und onError(), sodass wir mithilfe einer anderen Basisklasse bestimmen können, in welchen Bereichen Folgendes getan werden muss onNext():

Action1 onNextAction = new Action1() {
    @Override
    public void call(String s) {
        System.out.println(s);
    }
};

Actionkann verwendet werden, um ein beliebiges Teil zu ersetzen Subscriber: es Observable.subscribe()können ein, zwei oder drei ActionParameter verwendet werden, die stattdessen ausgeführt werden onNext(), onError()und onCompete(). Das heißt, wir können unsere so ersetzen Subscriber:

myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);

Da wir aber nicht onError()und brauchen onCompete(), können wir den Code noch weiter vereinfachen:

myObservable.subscribe(onNextAction);
// Выводит "Hello, world!"

Lassen Sie uns nun die Variablen loswerden, indem wir auf einen Aufruf der Kettenmethode zurückgreifen:

Observable.just("Hello, world!")
    .subscribe(new Action1() {
        @Override
        public void call(String s) {
              System.out.println(s);
        }
    });

Und schließlich können wir Lambdas aus Java 8 verwenden, um den Code noch weiter zu vereinfachen:

Observable.just("Hello, world!")
    .subscribe(s -> System.out.println(s));

Wenn Sie unter Android schreiben (und daher nicht Java verwenden können 8), mich wirklich empfehlen retrolambda , die an einigen Stellen sehr ausführlichen Code vereinfachen helfen.

Transformation


Probieren wir etwas Neues aus.
Ich möchte beispielsweise meine Signatur zu "Hallo Welt!" Hinzufügen, die in der Konsole angezeigt wird. Wie kann man das machen? Erstens können wir ändern Observable:

Observable.just("Hello, world! -Dan")
    .subscribe(s -> System.out.println(s));

Dies funktioniert möglicherweise, wenn Sie Zugriff auf den Quellcode haben, in dem Ihr Code definiert ist. Dies ist Observablejedoch nicht immer der Fall, wenn Sie beispielsweise die Bibliothek eines anderen Benutzers verwenden. Ein weiteres Problem: Was ist, wenn wir unsere Observablean vielen Orten verwenden, aber nur in einigen Fällen eine Signatur hinzufügen möchten ?
Sie können versuchen, neu zu schreiben Subscriber:

Observable.just("Hello, world!")
    .subscribe(s -> System.out.println(s + " -Dan"));

Diese Option ist ebenfalls unangemessen, aber aus anderen Gründen: Ich möchte, dass meine Abonnenten so leicht wie möglich sind, da ich sie im Haupt-Thread ausführen kann. Auf einer konzeptionelleren Ebene sollten Abonnenten auf die empfangenen Daten reagieren und diese nicht ändern .
Es wäre großartig, wenn Sie "Hallo Welt!" In einem Zwischenschritt ändern könnten.

Einführung in Operatoren


Und es gibt einen solchen Zwischenschritt für die Datentransformation. Sein Name ist operator und sie können zwischen Observableund Subscriberfür die Datenmanipulation verwendet werden. Es gibt viele Operatoren in RxJava, daher ist es für den Anfang besser, sich nur auf wenige zu konzentrieren.
Für unsere spezielle Situation ist ein Operator am besten geeignet, map()mit dem Sie ein Datenelement in ein anderes konvertieren können:

Observable.just("Hello, world!")
    .map(new Func1() {
        @Override
        public String call(String s) {
            return s + " -Dan";
        }
    })
    .subscribe(s -> System.out.println(s));

Und wieder können Sie auf Lambdas zurückgreifen:

Observable.just("Hello, world!")
    .map(s -> s + " -Dan")
    .subscribe(s -> System.out.println(s));

Cool, was? map()Grob gesagt ist unser Operator Observablederjenige, der das eingegebenen Datenelement transformiert. Wir können eine Kette von so vielen erstellen, map()wie wir für notwendig halten, um den Daten die bequemste und einfachste Form zu geben und unsere Aufgabe zu erleichtern Subscriber.

Noch etwas zu map ()


Eine interessante Eigenschaft map()ist, dass keine Daten des gleichen Typs wie das Original generiert werden müssen Observable.
Nehmen wir an, dass bei uns Subscribernicht der generierte Text, sondern sein Hash angezeigt werden soll:

Observable.just("Hello, world!")
    .map(new Func1() {
        @Override
        public Integer call(String s) {
            return s.hashCode();
        }
    })
    .subscribe(i -> System.out.println(Integer.toString(i)));

Interessant: Wir haben mit Zeilen begonnen und unsere Subscriberakzeptiert Integer. Übrigens haben wir Lambdas wieder vergessen:

Observable.just("Hello, world!")
    .map(s -> s.hashCode())
    .subscribe(i -> System.out.println(Integer.toString(i)));

Wie ich bereits sagte, möchten wir, dass wir Subscriberso wenig wie möglich arbeiten. Wenden wir also eine andere map()an, um unseren Hash zurück zu konvertieren String:

Observable.just("Hello, world!")
    .map(s -> s.hashCode())
    .map(i -> Integer.toString(i))
    .subscribe(s -> System.out.println(s));

Schauen Sie sich unsere an Observableund sehen Sie Subscriberjetzt genauso aus wie am Anfang! Wir haben nur ein paar Zwischenschritte hinzugefügt, um unsere Daten zu transformieren. Wir könnten sogar noch einmal Code hinzufügen und meine Signatur zu den generierten Zeilen hinzufügen:

Observable.just("Hello, world!")
    .map(s -> s + " -Dan")
    .map(s -> s.hashCode())
    .map(i -> Integer.toString(i))
    .subscribe(s -> System.out.println(s));

Und was dann?


Jetzt denken Sie wahrscheinlich: "Nun, wie üblich: Sie zeigen ein einfaches Beispiel und sie sagen, dass die Technologie cool ist, weil Sie dieses Problem in zwei Codezeilen lösen können." Ich stimme zu, das Beispiel ist wirklich einfach. Daraus lassen sich jedoch einige nützliche Ideen ableiten:

Idee 1: Observableund Subscribersie können alles machen


Grenzen Sie nicht Ihre Vorstellungskraft ein , alles, was Sie wollen, ist möglich .
Ihre Observablekann eine Abfrage an die Datenbank sein oder Subscriberdie Ergebnisse der Abfrage auf dem Bildschirm anzeigen. Observablekann auch ein Klick auf den Bildschirm sein, Subscriberkann eine Reaktion auf diesen Klick enthalten. Observablekann ein Strom von Bytes sein, die vom Netzwerk empfangen werden, während Subscriberdiese Daten auf ein Datenspeichergerät geschrieben werden können.
Dies ist ein Allzweck-Framework, das nahezu jedes Problem bewältigen kann.

Idee Nr. 2: Observableund Subscribernicht auf die Zwischenschritte zwischen ihnen angewiesen


Sie können so viele Anrufe einfügen, wie Sie möchten, und zwar map()zwischen Observabledenen, die Sie abonniert haben Subscriber. Das System ist leicht zusammensetzbar und mit seiner Hilfe ist es sehr einfach, den Datenfluss zu kontrollieren. Wenn die Operatoren mit den richtigen Eingabe- / Ausgabedaten arbeiten, können Sie eine Kette von Transformationen mit unendlicher Länge 4 schreiben .

Schauen Sie sich diese Schlüsselideen gemeinsam an und Sie werden ein System mit großem Potenzial sehen. Jetzt haben wir jedoch nur einen Operator map(), und Sie werden nicht viel damit schreiben. Im zweiten Teil dieses Artikels werden wir eine große Anzahl von Operatoren betrachten, die Ihnen sofort zur Verfügung stehen, wenn Sie RxJava verwenden.

Fahren Sie mit dem zweiten Teil fort.


1Subscriber implementiert die Schnittstelle Observer, und daher kann der „Grundbaustein“ höchstwahrscheinlich als letzterer bezeichnet werden. In der Praxis wird er jedoch am häufigsten verwendet Subscriber, da er mehrere zusätzliche nützliche Methoden enthält, darunter Subscriber.unsubscribe().
2 In RxJava ist „heiß“ und „kalt“ Observables. Hot Observablegeneriert ständig Daten, auch wenn niemand diese abonniert hat. Cold Observablegeneriert Daten nur dann, wenn mindestens ein Abonnent angemeldet ist (der Artikel verwendet Cold Observables). Für die Anfangsphasen des Lernens von RxJava ist dieser Unterschied nicht so wichtig.
3 Streng genommen handelt es sich Observable.just()nicht um eine vollständige Entsprechung unseres ursprünglichen Codes, sondern darum , warumdas passiert, werde ich erst im dritten teil des artikels erläutern.
4 Okay, nicht so endlos, denn irgendwann werde ich mich gegen die Einschränkungen durch Eisen ausruhen, aber Sie verstehen, was ich sagen wollte.

Jetzt auch beliebt: