Advertisement
  1. Code
  2. Android SDK

RxJava 2 für Android-Apps: RxBinding und RxLifecycle

by
Read Time:16 minsLanguages:

German (Deutsch) translation by Władysław Łucyszyn (you can also view the original English article)

RxJava ist eine der beliebtesten Bibliotheken, um reaktive Programmierung auf die Android-Plattform zu bringen, und in dieser dreiteiligen Serie zeige ich Ihnen, wie Sie diese Bibliothek in Ihren eigenen Android-Projekten verwenden können.

In Get Started With RxJava 2 for Android haben wir uns angesehen, was RxJava ist und was es Android-Entwicklern zu bieten hat, bevor wir eine Hello World-App erstellt haben, die die drei Kernkomponenten von RxJava demonstriert: ein Observable, ein Observer und ein Abonnement.

Im Tutorial Reaktive Programmieroperatoren in RxJava 2 haben wir uns angesehen, wie komplexe Datentransformationen mit Operatoren durchgeführt werden und wie Sie Operatoren und Scheduler kombinieren können, um Multithreading auf Android endlich zu einem problemlosen Erlebnis zu machen.

Wir haben auch RxAndroid angesprochen, eine Bibliothek, die speziell entwickelt wurde, um Ihnen bei der Verwendung von RxJava in Ihren Android-Projekten zu helfen, aber in RxAndroid gibt es noch viel mehr zu entdecken. In diesem Beitrag werde ich mich also ausschließlich auf die RxAndroid-Bibliothekenfamilie konzentrieren.

Ähnlich wie RxJava wurde RxAndroid in seiner Version 2 einer umfassenden Überarbeitung unterzogen. Das RxAndroid-Team beschloss, die Bibliothek zu modularisieren und einen Großteil ihrer Funktionalität in dedizierte RxAndroid-Add-On-Module zu verlagern.

In diesem Artikel zeige ich Ihnen, wie Sie einige der beliebtesten und leistungsstärksten RxAndroid-Module einrichten und verwenden – einschließlich einer Bibliothek, die Listener, Handler und TextWatcher der Vergangenheit angehören kann, indem sie Ihnen die Möglichkeit gibt, mit jedes Android-UI-Ereignis als Observable.

Und da Speicherlecks durch unvollständige Abonnements der größte Nachteil bei der Verwendung von RxJava in Ihren Android-Apps sind, zeige ich Ihnen auch, wie Sie ein RxAndroid-Modul verwenden, das den Abonnementprozess für Sie übernimmt. Am Ende dieses Artikels wissen Sie, wie Sie RxJava in jeder Activity oder jedem Fragment verwenden, ohne das Risiko einzugehen, auf RxJava-bezogene Speicherlecks zu stoßen.

Reaktivere Android-Benutzeroberflächen erstellen

Das Reagieren auf UI-Ereignisse wie Tippen, Wischen und Texteingabe ist ein grundlegender Bestandteil der Entwicklung so ziemlich jeder Android-App, aber der Umgang mit Android-UI-Ereignissen ist nicht besonders einfach.

In der Regel reagieren Sie auf UI-Ereignisse mit einer Kombination aus Listenern, Handlern, TextWatchers und möglicherweise anderen Komponenten, abhängig von der Art der UI, die Sie erstellen. Für jede dieser Komponenten müssen Sie eine beträchtliche Menge Boilerplate-Code schreiben, und zu allem Überfluss gibt es keine einheitliche Implementierung dieser verschiedenen Komponenten. Beispielsweise behandeln Sie OnClick-Ereignisse, indem Sie einen OnClickListener implementieren:

Dies ist jedoch völlig anders, als Sie einen TextWatcher implementieren würden:

Dieser Mangel an Konsistenz kann Ihren Code möglicherweise viel komplexer machen. Und wenn Sie UI-Komponenten haben, die von der Ausgabe anderer UI-Komponenten abhängen, dann machen Sie sich bereit, dass die Dinge noch komplizierter werden! Selbst ein einfacher Anwendungsfall – wie die Aufforderung an den Benutzer, seinen Namen in einen EditText einzugeben, damit Sie den in nachfolgenden TextViews angezeigten Text personalisieren können – erfordert verschachtelte Callbacks, die bekanntermaßen schwierig zu implementieren und zu warten sind. (Manche Leute bezeichnen verschachtelte Rückrufe als "Rückrufhölle".)

Ein standardisierter Ansatz zur Behandlung von UI-Ereignissen hat eindeutig das Potenzial, Ihren Code erheblich zu vereinfachen, und RxBinding ist eine Bibliothek, die genau das tut, indem sie Bindungen bereitstellt, mit denen Sie jedes Android View-Ereignis in ein Observable konvertieren können.

Sobald Sie ein Ansichtsereignis in ein Observable umgewandelt haben, gibt es seine UI-Ereignisse als Datenströme aus, die Sie genauso abonnieren können, wie Sie jedes andere Observable abonnieren würden.

Da wir bereits gesehen haben, wie Sie ein Klickereignis mit dem Standard-OnClickListener von Android erfassen, sehen wir uns an, wie Sie mit RxBinding dieselben Ergebnisse erzielen:

Dieser Ansatz ist nicht nur prägnanter, sondern auch eine Standardimplementierung, die Sie auf alle UI-Ereignisse anwenden können, die in Ihrer App auftreten. Das Erfassen von Texteingaben folgt beispielsweise dem gleichen Muster wie das Erfassen von Klickereignissen:

Eine Beispiel-App mit RxBinding

Damit Sie genau sehen können, wie RxBinding den UI-bezogenen Code Ihrer App vereinfachen kann, erstellen wir eine App, die einige dieser Bindungen in Aktion demonstriert. Ich werde auch eine View einfügen, die von der Ausgabe einer anderen View abhängt, um zu zeigen, wie RxBinding das Erstellen von Beziehungen zwischen UI-Komponenten vereinfacht.

Diese App wird bestehen aus:

  • Eine Button, die beim Antippen einen Toast anzeigt.
  • Ein EditText, der Textänderungen erkennt.
  • Eine TextView, die aktualisiert wird, um den Inhalt von EditText anzuzeigen.

Projektaufbau

Erstellen Sie ein Android Studio-Projekt mit den Einstellungen Ihrer Wahl, öffnen Sie dann die Datei build.gradle auf Modulebene und fügen Sie die neueste Version der RxBinding-Bibliothek als Projektabhängigkeit hinzu. Um den Boilerplate-Code auf ein Minimum zu beschränken, werde ich auch Lambdas verwenden, daher habe ich meine build.gradle-Datei aktualisiert, um diese Java 8-Funktion zu unterstützen:

Wenn Sie mit mehreren RxJava-Bibliotheken arbeiten, ist es möglich, dass Sie zur Kompilierzeit auf die Fehlermeldung Duplizierte Dateien kopiert in APK META-INF/DEPENDENCIES stoßen. Wenn dieser Fehler auftritt, besteht die Problemumgehung darin, diese doppelten Dateien zu unterdrücken, indem Sie der Datei build.gradle auf Modulebene Folgendes hinzufügen:

Erstellen Sie das Hauptaktivitätslayout

Synchronisieren Sie Ihre Gradle-Dateien und erstellen Sie dann ein Layout, das aus einem Button, einem EditText und einem TextView besteht:

Codieren Sie die Ereignisbindungen

Sehen wir uns nun an, wie Sie diese RxBinding verwenden würden, um die verschiedenen UI-Ereignisse zu erfassen, auf die unsere Anwendung reagieren muss. Deklarieren Sie zunächst Ihre Importe und definieren Sie die MainActivity-Klasse.

Jetzt können Sie Bindungen hinzufügen, um auf UI-Ereignisse zu reagieren. Die Methode RxView.clicks wird verwendet, um Klickereignisse zu binden. Erstellen Sie eine Bindung, um einen Toast anzuzeigen, wenn auf die Schaltfläche geklickt wird:

Verwenden Sie als Nächstes die Methode RxTextView.textChanges(), um auf ein Textänderungsereignis zu reagieren, indem Sie die TextView mit dem Inhalt unseres EditText aktualisieren.

Wenn Sie Ihre App ausführen, erhalten Sie am Ende einen Bildschirm wie den folgenden.

The default version of our RxBinding user interface The default version of our RxBinding user interface The default version of our RxBinding user interface

Installieren Sie Ihr Projekt auf einem physischen Android-Smartphone oder -Tablet oder einem kompatiblen AVD und verbringen Sie dann einige Zeit damit, mit den verschiedenen UI-Elementen zu interagieren. Ihre App sollte ganz normal auf Klickereignisse und Texteingaben reagieren – und das alles ohne Hörer, TextWatcher oder Rückruf!

RxBinding user interfaceRxBinding user interfaceRxBinding user interface

RxBinding für Supportbibliotheksansichten

Während die RxBinding-Kernbibliothek Bindungen für alle UI-Elemente bereitstellt, aus denen die Standard-Android-Plattform besteht, gibt es auch RxBinding-Geschwistermodule, die Bindungen für die Ansichten bereitstellen, die als Teil der verschiedenen Unterstützungsbibliotheken von Android enthalten sind.

Wenn Sie Ihrem Projekt eine oder mehrere Supportbibliotheken hinzugefügt haben, möchten Sie normalerweise auch das entsprechende RxBinding-Modul hinzufügen.

Diese Geschwistermodule folgen einer einfachen Namenskonvention, die es einfach macht, die entsprechende Android-Supportbibliothek zu identifizieren: Jedes Geschwistermodul nimmt einfach den Namen der Supportbibliothek an und ersetzt com.android durch com.jakewharton.rxbinding2:rxbinding.

  • compile com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'
  • compile 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0'
  • compile 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0'
  • compile 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0'
  • compile 'com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'
  • compile 'com.jakewharton.rxbinding2:rxbinding-leanback-v17:2.0.0'

Wenn Sie Kotlin in Ihren Android-Projekten verwenden, steht für jedes RxBinding-Modul auch eine Kotlin-Version zur Verfügung. Um auf die Kotlin-Version zuzugreifen, hängen Sie einfach -kotlin an den Namen der Bibliothek an, mit der Sie arbeiten möchten, also:

Wird:

Nachdem Sie ein View-Ereignis in ein Observable umgewandelt haben, werden alle diese Ereignisse als Datenstrom ausgegeben. Wie wir bereits gesehen haben, können Sie diese Streams abonnieren und dann jede Aufgabe ausführen, die dieses bestimmte UI-Ereignis auslösen soll, z. B. das Anzeigen eines Toasts oder das Aktualisieren einer TextView. Sie können jedoch auch jede der enormen Operatoren von RxJava auf diesen beobachtbaren Stream anwenden und sogar mehrere Operatoren miteinander verketten, um komplexe Transformationen an Ihren UI-Ereignissen durchzuführen.

Es gibt viel zu viele Operatoren, um in einem einzigen Artikel zu diskutieren (und die offiziellen Dokumente listen sowieso alle Operatoren auf), aber wenn es um die Arbeit mit Android-UI-Ereignissen geht, gibt es einige Operatoren, die besonders nützlich sein können.

Der debounce()-Operator

Erstens, wenn Sie befürchten, dass ein ungeduldig Benutzer wiederholt auf ein UI-Element tippen könnte, was Ihre App möglicherweise verwirren könnte, können Sie den debounce()-Operator verwenden, um alle UI-Ereignisse herauszufiltern, die in schneller Folge ausgegeben werden.

Im folgenden Beispiel gebe ich an, dass diese Schaltfläche nur dann auf ein OnClick-Ereignis reagieren soll, wenn seit dem vorherigen Klickereignis mindestens eine Lücke von 500 Millisekunden aufgetreten ist:

Der publish()-Operator

Sie können auch den publish()-Operator verwenden, um mehrere Listener an dieselbe Ansicht anzuhängen, was in Android traditionell schwierig zu implementieren war.

Der publish()-Operator wandelt ein Standard-Observable in ein verbindbares Observable um. Während ein reguläres Observable mit der Ausgabe von Elementen beginnt, sobald der erste Beobachter es abonniert hat, wird ein verbindbares Observable nichts ausgeben, bis Sie es explizit anweisen, indem Sie den connect()-Operator anwenden. Dies gibt Ihnen ein Zeitfenster, in dem Sie mehrere Beobachter abonnieren können, ohne dass das Observable sofort nach dem ersten Abonnement mit der Ausgabe von Elementen beginnt.

Sobald Sie alle Ihre Abonnements erstellt haben, wenden Sie einfach den connect()-Operator an und das Observable beginnt mit der Ausgabe von Daten an alle ihm zugewiesenen Beobachter.

Vermeiden Sie App-brechende Speicherlecks

Wie wir in dieser Serie gesehen haben, kann RxJava ein leistungsstarkes Werkzeug sein, um reaktivere, interaktivere Android-Anwendungen mit viel weniger Code zu erstellen, als Sie normalerweise benötigen, um die gleichen Ergebnisse allein mit Java zu erzielen. Es gibt jedoch einen großen Nachteil bei der Verwendung von RxJava in Ihren Android-Anwendungen – die Möglichkeit von Speicherlecks, die durch unvollständige Abonnements verursacht werden.

Diese Speicherverluste treten auf, wenn das Android-System versucht, eine Activity zu zerstören, die ein laufendes Observable enthält. Da das Observable ausgeführt wird, hält sein Beobachter immer noch einen Verweis auf die Aktivität, und das System kann diese Aktivität daher nicht nach dem Garbage-Collector sammeln.

Da Android jedes Mal, wenn sich die Konfiguration des Geräts ändert, Activity zerstört und neu erstellt, erstellt Ihre App möglicherweise jedes Mal, wenn der Benutzer zwischen Hoch- und Querformat wechselt, sowie jedes Mal, wenn er die Tastatur seines Geräts öffnet und schließt, eine doppelte Activity.

Diese Aktivitäten werden im Hintergrund herumhängen und möglicherweise nie Müll sammeln. Da Aktivitäten große Objekte sind, kann dies schnell zu ernsthaften Problemen bei der Speicherverwaltung führen, zumal Android-Smartphones und -Tablets anfangs nur begrenzten Speicher haben. Die Kombination aus einem großen Speicherverlust und begrenztem Speicher kann schnell zu einem Fehler wegen unzureichendem Speicher führen.

RxJava-Speicherlecks können die Leistung Ihrer Anwendung beeinträchtigen, aber es gibt eine RxAndroid-Bibliothek, mit der Sie RxJava in Ihrer App verwenden können, ohne sich um Speicherlecks sorgen zu müssen.

Die von Trello entwickelte RxLifecycle-Bibliothek bietet APIs zur Lebenszyklusbehandlung, mit denen Sie die Lebensdauer eines Observables auf den Lebenszyklus einer Activity oder eines Fragment begrenzen können. Sobald diese Verbindung hergestellt ist, beendet RxLifecycle die Sequenz des Observables als Reaktion auf Lebenszyklusereignisse, die in der zugewiesenen Aktivität oder dem Fragment dieses Observables auftreten. Dies bedeutet, dass Sie ein Observable erstellen können, das automatisch beendet wird, wenn eine Aktivität oder ein Fragment zerstört wird.

Beachten Sie, dass wir über das Beenden einer Sequenz sprechen und nicht über das Abmelden. Obwohl RxLifecycle oft im Zusammenhang mit der Verwaltung des An- und Abmeldeprozesses erwähnt wird, wird ein Beobachter technisch gesehen nicht abgemeldet. Stattdessen beendet die RxLifecycle-Bibliothek die beobachtbare Sequenz, indem sie entweder die Methode onComplete() oder onError() ausgibt. Wenn Sie sich abmelden, erhält der Beobachter keine Benachrichtigungen von seiner Observablen, selbst wenn diese Observable noch Elemente ausgibt. Wenn Sie ein spezielles Abmeldeverhalten benötigen, müssen Sie dies selbst implementieren.

Verwenden von RxLifecycle

Um RxLifecycle in Ihren Android-Projekten zu verwenden, öffnen Sie Ihre Datei build.gradle auf Modulebene und fügen Sie die neueste Version der RxLifeycle-Bibliothek sowie die RxLifecycle-Android-Bibliothek hinzu:

Erweitern Sie dann in der Activity oder dem Fragment, in dem Sie die Lebenszyklus-Handling-APIs der Bibliothek verwenden möchten, entweder RxActivity, RxAppCompatActivity oder RxFragment und fügen Sie die entsprechende Importanweisung hinzu, zum Beispiel:

Wenn es darum geht, ein Observable an den Lebenszyklus einer Activity oder eines Fragment zu binden, können Sie entweder das Lebenszyklusereignis angeben, bei dem das Observable beendet werden soll, oder Sie können die RxLifecycle-Bibliothek entscheiden lassen, wann sie die Observable-Sequenz beenden soll.

Standardmäßig beendet RxLifecycle ein Observable in dem komplementären Lebenszyklusereignis zu dem, in dem dieses Abonnement stattfand. Wenn Sie also ein Observable während der onCreate()-Methode Ihrer Aktivität abonnieren, beendet RxLifecycle die Observable-Sequenz während der onDestroy()-Methode dieser Aktivität. Wenn Sie während der onAttach()-Methode eines Fragments abonnieren, beendet RxLifecycle diese Sequenz in der onDetach()-Methode.

Sie können diese Entscheidung RxLifecycle überlassen, indem Sie RxLifecycleAndroid.bindActivity verwenden:

Alternativ können Sie mit RxLifecycle.bindUntilEvent das Lebenszyklusereignis angeben, bei dem RxLifecycle eine Observable-Sequenz beenden soll.

Hier gebe ich an, dass die beobachtbare Sequenz in onDestroy() beendet werden soll:

Arbeiten mit Android Marshmallow-Berechtigungen

Die letzte Bibliothek, die wir uns ansehen werden, ist RxPermissions, die Ihnen helfen soll, RxJava mit dem neuen Berechtigungsmodell zu verwenden, das in Android 6.0 eingeführt wurde. Mit dieser Bibliothek können Sie auch eine Berechtigungsanforderung ausgeben und das Berechtigungsergebnis am selben Ort verarbeiten, anstatt die Berechtigung an einem Ort anzufordern und ihre Ergebnisse dann separat in Activity.onRequestPermissionsResult() zu verarbeiten.

Beginnen Sie mit dem Hinzufügen der RxPermissions-Bibliothek zu Ihrer build.gradle-Datei:

Erstellen Sie dann eine RxPermissions-Instanz:

Sie können dann mit der folgenden Formel über die RxPermissions-Bibliothek Berechtigungsanfragen stellen:

Es ist entscheidend, wo Sie Ihre Berechtigungsanfrage stellen, da immer die Möglichkeit besteht, dass die Hosting-Activity zerstört und dann neu erstellt wird, während der Berechtigungsdialog auf dem Bildschirm angezeigt wird, normalerweise aufgrund einer Konfigurationsänderung, z. B. wenn der Benutzer zwischen Hoch- und Querformat wechselt. In diesem Fall wird Ihr Abonnement möglicherweise nicht neu erstellt, was bedeutet, dass Sie das Observable RxPermissions nicht abonnieren und keine Antwort des Benutzers auf den Berechtigungsanfragedialog erhalten. Um sicherzustellen, dass Ihre Anwendung die Antwort des Benutzers erhält, rufen Sie Ihre Anfrage immer während einer Initialisierungsphase wie Activity.onCreate(), Activity.onResume() oder View.onFinishInflate() auf.

Es ist nicht ungewöhnlich, dass Funktionen mehrere Berechtigungen erfordern. Das Senden einer SMS-Nachricht erfordert beispielsweise normalerweise, dass Ihre App über die Berechtigungen SEND_SMS und READ_CONTACTS verfügt. Die RxPermissions-Bibliothek bietet eine präzise Methode zum Ausgeben mehrerer Berechtigungsanforderungen und zum anschließenden Kombinieren der Antworten des Benutzers zu einer einzigen false (eine oder mehrere Berechtigungen wurden verweigert) oder true (alle Berechtigungen wurden gewährt), auf die Sie dann entsprechend reagieren können.

Normalerweise möchten Sie eine Berechtigungsanfrage als Reaktion auf ein UI-Ereignis auslösen, z. B. wenn der Benutzer auf ein Menüelement oder eine Schaltfläche tippt. Daher sind RxPermissions und RxBiding zwei Bibliotheken, die besonders gut zusammenarbeiten.

Wenn Sie das UI-Ereignis als Observable behandeln und die Berechtigungsanfrage über RxPermissions stellen, können Sie mit nur wenigen Codezeilen viel Arbeit ausführen:

Abschluss

Nachdem Sie diesen Artikel gelesen haben, haben Sie einige Ideen, wie Sie viel Boilerplate-Code aus Ihren Android-Apps herausschneiden können – indem Sie RxJava verwenden, um alle UI-Ereignisse Ihrer Anwendung zu verarbeiten, und Ihre Berechtigungsanfragen über RxPermissions ausgeben. Wir haben uns auch angesehen, wie Sie RxJava in jeder Android-Activity oder jedem Fragment verwenden können, ohne sich um Speicherlecks sorgen zu müssen, die durch unvollständige Abonnements verursacht werden können.

Wir haben einige der beliebtesten und nützlichsten RxJava- und RxAndroid-Bibliotheken in dieser Serie untersucht, aber wenn Sie wissen möchten, was RxJava Android-Entwicklern sonst noch zu bieten hat, sehen Sie sich einige der vielen anderen RxAndroid-Bibliotheken an. Eine umfassende Liste zusätzlicher RxAndroid-Bibliotheken finden Sie auf GitHub.

Sehen Sie sich in der Zwischenzeit einige unserer anderen Android-Entwicklungsbeiträge hier auf Envato Tuts+ an!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.