RxJava 2.0 ist eine beliebte reaktive Programmierbibliothek, die unzähligen Android-Entwicklern dabei geholfen hat, schnell reagierende Apps mit weniger Code und geringerer Komplexität zu erstellen, insbesondere wenn es darum geht, mehrere Threads zu verwalten.
Wenn Sie zu den vielen Entwicklern gehören, die zu Kotlin gewechselt haben, heißt das nicht, dass Sie RxJava aufgeben müssen!
Im ersten Teil dieser Serie habe ich Ihnen gezeigt, wie Sie von der Programmierung mit RxJava 2.0 in Java zur Programmierung mit RxJava in wechseln Kotlin. Außerdem haben wir untersucht, wie Sie Boilerplate aus Ihren Projekten verbannen können, indem Sie die Erweiterungsfunktionen von RxKotlin nutzen. Außerdem erfahren Sie, wie Sie das SAM-Konvertierungsproblem umgehen können, dem viele Entwickler beim ersten Einsatz von RxJava 2.0 mit Kotlin begegnen.
In diesem zweiten Teil konzentrieren wir uns darauf, wie RxJava helfen kann, die Probleme zu lösen, die bei echten Android-Projekten auftreten, indem wir eine reaktive Android-Anwendung mit RxJava 2.0, RxAndroid und RxBinding erstellen.
In unserem Artikel über reaktive Programmierung mit RxJava und RxKotlin haben wir einige einfache Funktionen erstellt Observables
und Beobachter
die Druckdaten auf Android Studio Logcat-Aber so werden Sie RxJava in der realen Welt nicht verwenden.
In diesem Artikel werde ich Ihnen zeigen, wie Sie mit RxJava einen Bildschirm erstellen, der in unzähligen Android-Anwendungen verwendet wird: dem Klassiker Anmelden Bildschirm.
Wenn Ihre App hat irgendein Art der Anmeldeerfahrung, dann hat es normalerweise strenge Regeln für die Art der Informationen, die es akzeptiert. Beispielsweise muss das Kennwort möglicherweise eine bestimmte Anzahl von Zeichen überschreiten oder die E-Mail-Adresse muss ein gültiges E-Mail-Format haben.
Während du könnte Überprüfen Sie die Eingaben des Benutzers, sobald Sie auf die Schaltfläche klicken Anmelden Diese Schaltfläche ist nicht die beste Benutzererfahrung, da sie Informationen übermitteln kann, die von Ihrer Anwendung offensichtlich nicht akzeptiert werden.
Es ist weitaus besser, den Benutzer beim Tippen zu überwachen und ihm dann ein Heads-up zu geben, sobald klar wird, dass er Informationen eingibt, die den Anforderungen Ihrer App nicht entsprechen. Durch diese Art von Live-Feedback und laufendem Feedback geben Sie dem Benutzer die Möglichkeit, seine Fehler zu korrigieren Vor das zu schlagen Anmelden Taste.
Während du könnte Überwachen Sie die Benutzeraktivität mit Vanilla Kotlin. Wir können diese Funktionalität mit viel weniger Code bereitstellen, indem Sie die Hilfe von RxJava sowie einige andere verwandte Bibliotheken verwenden.
Beginnen wir mit dem Aufbau unserer Benutzeroberfläche. Ich werde folgendes hinzufügen:
EditTexts
, wo der Benutzer seine E-Mail-Adresse eingeben kann (Email eingeben
) und Passwort (Passwort eingeben
).TextInputLayout
Verpackungen, die unsere umgeben werden Email eingeben
und Passwort eingeben
EditTexts
. Diese Wrapper zeigen eine Warnung an, wenn der Benutzer eine E-Mail-Adresse oder ein Kennwort eingibt, die nicht den Anforderungen unserer App entsprechen.Hier ist mein fertiges Layout:
Sie können dies kopieren und in Ihre App einfügen, wenn Sie möchten, oder Sie können den Quellcode des Projekts von unserem GitHub-Repo herunterladen.
Sehen wir uns nun an, wie wir RxJava und einige zugehörige Bibliotheken verwenden können, um Benutzereingaben zu überwachen und in Echtzeit Feedback zu geben.
Ich werde das anpacken Anmelden Bildschirm in zwei Teilen. Im ersten Abschnitt zeige ich Ihnen, wie Sie die RxBinding-Bibliothek zum Registrieren und Reagieren auf Textänderungsereignisse verwenden. Im zweiten Abschnitt erstellen wir einige Transformationsfunktionen, die die Eingaben des Benutzers überprüfen und gegebenenfalls eine Fehlermeldung anzeigen.
Erstellen Sie ein neues Projekt mit den Einstellungen Ihrer Wahl, aber wenn Sie dazu aufgefordert werden, stellen Sie sicher, dass Sie die Option auswählen Kotlin-Support einbeziehen Kontrollkästchen.
In diesem Abschnitt implementieren wir die folgende Funktionalität:
Email eingeben
Feld.Toast
. RxBinding ist eine Bibliothek, die das Konvertieren einer großen Anzahl von UI-Ereignissen in Observables vereinfacht. Sie können sie dann wie jeden anderen RxJava-Datenstrom behandeln.
Wir werden Textänderungsereignisse überwachen, indem wir die von RxBinding kombinieren widget.RxTextView
mit dem afterTextChangeEvents
Methode zum Beispiel:
RxTextView.afterTextChangeEvents (enterEmail)
Das Problem bei der Behandlung von Textänderungsereignissen als Datenströme ist, dass zunächst beide Email eingeben
und enterPassword EditTexts
wird leer sein, und wir möchten nicht, dass unsere App auf diesen leeren Status reagiert, als wäre dies die erste Datenemission im Stream. RxBinding löst dieses Problem, indem Sie a skipInitialValue ()
Diese Methode wird verwendet, um jeden Observer anzuweisen, den ursprünglichen Wert des Streams zu ignorieren.
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue ()
Ich sehe mir die RxBinding-Bibliothek in meinem Artikel zu RxJava 2 für Android-Apps genauer an.
.debounce ()
OperatorUm die bestmögliche Benutzererfahrung zu bieten, müssen wir alle relevanten Kennwort- oder E-Mail-Warnungen anzeigen, nachdem der Benutzer die Eingabe abgeschlossen hat, jedoch bevor der Benutzer auf die Schaltfläche "." Anmelden Taste.
Ohne RxJava müssten wir normalerweise ein a implementieren, um dieses enge Zeitfenster zu identifizieren Timer
, aber in RxJava müssen wir nur die debounce ()
Betreiber zu unserem Datenstrom.
Ich werde das verwenden debounce ()
Operator, um alle Textänderungsereignisse herauszufiltern, die in schneller Folge auftreten, d. h. wenn der Benutzer noch tippt. Hier ignorieren wir alle Textänderungsereignisse, die innerhalb desselben 400-Millisekunden-Fensters stattfinden:
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS)
AndroidSchedulers.mainThread ()
Die RxAndroid-Bibliothek AndroidSchedulers.mainThread
bietet uns eine einfache Möglichkeit, zum wichtigsten UI-Thread von Android zu wechseln.
Da es nur möglich ist, die Android-Benutzeroberfläche über den Haupt-UI-Thread zu aktualisieren, müssen wir sicherstellen, dass wir uns in diesem Thread befinden, bevor wir versuchen, E-Mail- oder Kennwortwarnungen anzuzeigen, und bevor wir unsere anzeigen Toast
.
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ())
Um die Daten zu empfangen, die von gesendet werden Email eingeben
, wir müssen es abonnieren:
RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .subscribe
Schließlich möchten wir, dass unsere Anwendung auf Textänderungsereignisse reagiert, indem sie die Eingaben des Benutzers überprüft. Um die Dinge jedoch einfacher zu halten, werde ich an dieser Stelle einfach ein a anzeigen Toast
.
Ihr Code sollte ungefähr so aussehen:
import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.widget.Toast import com.jakewharton.rxbinding2.widget.RxTextView import kotlinx.android.synthetic.main.activity_main. * importieren .schadulatoren.AndroidSchedulers importieren die java.util.concurrent.TimeUnit-Klasse MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState: Bundle?) super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) RachTailaday (). skipInitialValue () .debounce (400, TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .subscribe Toast.makeText (dies "400 Millisekunden seit letzter Textänderung", Toast.LENGTH_SHORT).
Da wir ein paar verschiedene Bibliotheken verwenden, müssen wir unsere Projekte öffnen build.gradle Datei und fügen Sie RxJava, RxBinding und RxAndroid als Projektabhängigkeiten hinzu:
Abhängigkeiten Implementierungsdateibaum (Verzeichnis: 'libs', include: ['* .jar']) Implementierung "org.jetbrains.kotlin: kotlin-stdlib-jdk7: $ kotlin_version" Implementierung "com.android.support:design:28.0. 0-alpha1 'Implementierung' com.android.support:appcompat-v7:28.0.0-alpha1 'Implementierung' com.android.support.constraint: constraint-layout: 1.1.0 '// Hinzufügen der RxJava-Abhängigkeit // Implementierung' io.reactivex.rxjava2: rxjava: 2.1.9 '// Hinzufügen der RxAndroid-Abhängigkeit // Implementierung' io.reactivex.rxjava2: rxandroid: 2.0.2 '// Hinzufügen der RxBinding-Abhängigkeit // Implementierung' com.jakewharton.rxbinding2: rxbinding: 2.1.1 '
Sie können diesen Teil Ihres Projekts testen, indem Sie ihn auf Ihrem physischen Android-Smartphone oder -Tablet oder auf dem Android Virtual Device (AVD) installieren. Wähle aus Email eingeben
Text bearbeiten
und anfangen zu tippen; ein Toast
sollte angezeigt werden, wenn Sie mit dem Tippen aufhören.
Als Nächstes müssen wir einige Grundregeln für die Art der Eingabe festlegen, die unsere Anwendung akzeptiert, und dann die Eingabe des Benutzers anhand dieser Kriterien prüfen und gegebenenfalls eine Fehlermeldung anzeigen.
Die Überprüfung der E-Mail-Adresse oder des Passworts des Benutzers ist ein mehrstufiger Prozess. Um den Code lesbarer zu machen, kombiniere ich alle diese Schritte in ihre eigenen Transformationsfunktion.
Hier ist der Beginn des E-Mail validieren
Transformationsfunktion:
// Definiere einen ObservableTransformer. Eingabe und Ausgabe müssen eine Zeichenfolge sein, // private val validateEmailAddress = ObservableTransformerobservable -> // Verwenden Sie flatMap, um eine Funktion auf jedes Element anzuwenden, das von der Observable // observable.flatMap ausgegeben wird. // // Trennen Sie alle Leerzeichen am Anfang und Ende der Benutzereingabe. // Observable.just (it) .map it.trim () // Überprüfen Sie, ob die Eingabe mit dem E-Mail-Muster von Android übereinstimmt // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ()
Im obigen Code verwenden wir die Filter()
Operator, um die Ausgabe des Observable danach zu filtern, ob es mit dem von Android übereinstimmt Patterns.EMAIL_ADDRESS
Muster.
Im nächsten Teil der Transformationsfunktion müssen wir angeben, was passiert, wenn die Eingabe nicht mit dem übereinstimmt E-MAIL-ADDRESSE
Muster. Standardmäßig löst jeder nicht behebbare Fehler einen Aufruf an aus onError ()
, was den Datenstrom beendet. Anstatt den Stream zu beenden, möchten wir, dass unsere Anwendung eine Fehlermeldung anzeigt, also werde ich sie verwenden onErrorResumeNext
, Dadurch wird das Observable angewiesen, auf einen Fehler zu reagieren, indem die Kontrolle an ein neues Observable übergeben wird, anstatt aufzurufen onError ()
. Dadurch können wir unsere benutzerdefinierte Fehlermeldung anzeigen.
// Wenn die Eingabe des Benutzers nicht mit dem E-Mail-Muster übereinstimmt, wird ein Fehler ausgegeben. // .singleOrError () .onErrorResumeNext if (NoSuchElementException) Single.error (Ausnahme ("Bitte geben Sie eine gültige E-Mail-Adresse ein")) else Single.error (it) .toObservable ()
Der abschließende Schritt besteht darin, diese Transformationsfunktion mithilfe des Befehls auf den E-Mail-Datenstrom anzuwenden .komponieren()
Operator. An diesem Punkt dein MainActivity.kt sollte ungefähr so aussehen:
import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns importieren io.reactivex.Observable import io.reactivex.ObservableTransformer importieren io.reactivex.Single importieren io.reactivex.android.schedulers.AndroidSulated import kotlinx.android.synthetic.main.activity_main. * import java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView-Klasse MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState:) (savedInstanceState) setContentView (R.layout.activity_main) RxTextView.afterTextChangeEvents (enterEmail) .skipInitialValue () .map emailError.error = null it.view (). text.toString () .debounce (400) // Wir befinden uns im Android-UI-Thread // TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .compose (validateEmailAddress) .compose (retryWhenError passwordError.error = it.message) .subscribe () // if Die App stößt auf einen Fehler. Versuchen Sie es dann erneut mit // private Inline-Funktion n retryWhenError (crossinline onError: (ex: Throwable) -> Unit): ObservableTransformer= ObservableTransformer observable -> observable.retryWhen errors -> // Verwenden Sie den flatmap () - Operator, um alle Emissionen in einem einzigen Observable-Element zu reduzieren. // errors.flatMap onError (it) Observable.just ("") / / Definiere einen ObservableTransformer, wo wir die E-Mail-Validierung durchführen // private val validateEmailAddress = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Prüfen Sie, ob die Benutzereingabe mit dem E-Mail-Muster von Android übereinstimmt // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ( ) // Wenn die Eingabe des Benutzers nicht mit dem E-Mail-Muster übereinstimmt, wird eine Fehlermeldung ausgegeben. // .singleOrError () .onErrorResumeNext if (NoSuchElementException) Single.error (Ausnahme ("Bitte geben Sie eine gültige E-Mail-Adresse ein.") )) else Single.error (it) .toObservable ()
Installieren Sie dieses Projekt auf Ihrem Android-Gerät oder AVD, und Sie werden feststellen, dass der E-Mail-Teil der Anmelden Der Bildschirm überprüft nun Ihre Eingabe erfolgreich. Versuchen Sie, etwas anderes als eine E-Mail-Adresse einzugeben. Die App wird Sie darauf hinweisen, dass dies keine gültige Eingabe ist.
An diesem Punkt haben wir ein voll funktionierendes Email eingeben
Feld- und Umsetzung Passwort eingeben
ist meistens nur ein Fall der Wiederholung der gleichen Schritte.
In der Tat ist der einzige große Unterschied, dass unser validatePassword
Die Transformationsfunktion muss nach verschiedenen Kriterien suchen. Ich werde angeben, dass das Passwort des Benutzers mindestens 7 Zeichen lang sein muss:
.filter it.length> 7
Nachdem alle vorherigen Schritte wiederholt wurden, ist der Vorgang abgeschlossen MainActivity.kt sollte ungefähr so aussehen:
import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Patterns importieren io.reactivex.Observable import io.reactivex.ObservableTransformer importieren io.reactivex.Single importieren io.reactivex.android.schedulers.AndroidSulated import kotlinx.android.synthetic.main.activity_main. * import java.util.concurrent.TimeUnit import com.jakewharton.rxbinding2.widget.RxTextView-Klasse MainActivity: AppCompatActivity () override fun onCreate (savedInstanceState:) (savedInstanceState) setContentView (R.layout.activity_main) // Reagieren Sie auf Textänderungsereignisse in enterEmail // RxTextView.afterTextChangeEvents (enterEmail) // Überspringen Sie den initialen, leeren Zustand von enterEmail // .skipInitialValue () // Transformieren Sie die gesendeten Daten / / .map emailError.error = null // Konvertiere die Benutzereingabe in einen String // it.view (). text.toString () // // Ignoriere alle Emissionen, die innerhalb eines Zeitraums von 400 Millisekunden auftreten. // .debounce (400 // Vergewissere dich, dass wir uns im Android-UI-Thread // Tim befinden eUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) // Die Transformationsfunktion validateEmailAddress // .compose (validateEmailAddress) // anwenden Die Transformationsfunktion retryWhenError // .compose (retryWhenError itError.error itError.error) .subscribe () // Spülen und wiederholen Sie das enterPassword-EditText // RxTextView.afterTextChangeEvents (enterPassword) .skipInitialValue () .map passwordError.error = null it.view (). text.toString () .debounce (400) TimeUnit.MILLISECONDS) .observeOn (AndroidSchedulers.mainThread ()) .compose (validatePassword) .compose (retryWhenError passwordError.error = it.message) .subscribe () // Wenn die App einen Fehler aufweist, versuchen Sie es erneut / / private Inline Fun retryWhenError (crossinline onError: (ex: Throwable) -> Unit): ObservableTransformer= ObservableTransformer observable -> observable.retryWhen errors -> /// Verwenden Sie den flatmap () - Operator, um alle Emissionen in eine einzige beobachtbare // errors.flatMap onError (it) Observable.just ("") zu reduzieren. // Definieren Sie unseren ObservableTransformer und geben Sie an, dass die Eingabe und Ausgabe eine Zeichenfolge sein muss. // private val validatePassword = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Erlauben Sie nur Kennwörter mit mindestens 7 Zeichen // .filter it.length> 7 // Wenn das Das Kennwort ist weniger als 7 Zeichen. Dann wird ein Fehler ausgegeben. // .singleOrError () // Wenn ein Fehler auftritt… // .onErrorResumeNext if (es handelt sich um NoSuchElementException) // Zeigt die folgende Meldung im KennwortError-TextInputLayout an // Single. error (Ausnahme ("Ihr Kennwort muss aus mindestens 7 Zeichen bestehen")) else Single.error (it) .toObservable () // Definieren Sie einen ObservableTransformer, in dem die E-Mail-Validierung // privat ausgeführt wird val validateEmailAddress = ObservableTransformer observable -> observable.flatMap Observable.just (it) .map it.trim () // Prüfen Sie, ob die Benutzereingabe mit dem E-Mail-Muster von Android übereinstimmt // .filter Patterns.EMAIL_ADDRESS.matcher (it) .matches ( ) // Wenn die Eingabe des Benutzers nicht mit dem E-Mail-Muster übereinstimmt… // .singleOrError () .onErrorResumeNext if (NoSuchElementException) //// Zeigt die folgende Nachricht in emailError an TextInputLayout // Single.error ( Ausnahme ("Bitte geben Sie eine gültige E-Mail-Adresse ein")) else Single.error (it) .toObservable ()
Installieren Sie dieses Projekt auf Ihrem Android-Gerät oder AVD und experimentieren Sie mit der Eingabe in der Email eingeben
und Passwort eingeben
Felder. Wenn Sie einen Wert eingeben, der den Anforderungen der App nicht entspricht, wird die entsprechende Warnmeldung angezeigt, ohne Sie müssen auf die Anmelden Taste.
Sie können dieses komplette Projekt von GitHub herunterladen.
In diesem Artikel haben wir uns angesehen, wie RxJava helfen kann, die realen Probleme zu lösen, auf die Sie bei der Entwicklung Ihrer eigenen Android-Anwendungen stoßen, indem Sie RxJava 2.0, RxBinding und RxAndroid verwenden, um eine Anmelden Bildschirm.
Weitere Hintergrundinformationen zur RxJava-Bibliothek finden Sie in unserem Artikel Erste Schritte mit RxJava 2.0.