Mobius ist ein funktionales reaktives Framework zur Verwaltung von Zustandsentwicklungen und Nebenwirkungen mit Add-ons für die Verbindung zu Android-Benutzeroberflächen und RxJava-Observables. Der Schwerpunkt liegt auf der Trennung von Bedenken, der Testbarkeit und der Isolierung zustandsbehafteter Teile des Codes.
Um mehr zu erfahren, besuchen Sie die Website für eine Bedienungsanleitung. Um Mobius in Aktion zu sehen, sehen Sie sich die TODO-Beispiel-App an, die auf der App von Android Architecture Blueprints basiert. Sie können sich auch einen Vortrag von Android @Scale ansehen, in dem Mobius vorgestellt wird.
Mobius befindet sich im Produktionsstatus, was bedeutet, dass es in der Produktion in Spotify-Android-Anwendungen verwendet wird und wir die APIs als stabil und die Implementierung als fehlerfrei betrachten. Wir werden keine Änderungen vornehmen, die die Abwärtskompatibilität beeinträchtigen.
Mobius wird derzeit für Java 7 erstellt (da Java 8 nicht auf allen Android-Versionen vollständig unterstützt wird), weshalb einige in java.util.function
definierte Konzepte dupliziert werden (siehe com.spotify.mobius.functions
).
Bei der Verwendung von Mobius empfehlen wir die Verwendung von Kotlin oder Java 8 oder höher, vor allem wegen der verbesserten Typinferenz und weil die Verwendung von Lambdas die Lesbarkeit und Prägnanz des Codes erheblich verbessert.
Die neueste Version von Mobius ist über Maven Central verfügbar (LATEST_RELEASE unten ist):
implementation ' com.spotify.mobius:mobius-core:LATEST_RELEASE '
testImplementation ' com.spotify.mobius:mobius-test:LATEST_RELEASE '
implementation ' com.spotify.mobius:mobius-rx:LATEST_RELEASE ' // only for RxJava 1 support
implementation ' com.spotify.mobius:mobius-rx2:LATEST_RELEASE ' // only for RxJava 2 support
implementation ' com.spotify.mobius:mobius-rx3:LATEST_RELEASE ' // only for RxJava 3 support
implementation ' com.spotify.mobius:mobius-android:LATEST_RELEASE ' // only for Android support
implementation ' com.spotify.mobius:mobius-extras:LATEST_RELEASE ' // utilities for common patterns
Dies ist der Kern von Mobius, von dem alle anderen Module abhängen. Es handelt sich um eine reine Java-Bibliothek, die vollständig in sich geschlossen ist. Dies ist das einzige Modul, das Sie benötigen, wenn Sie Mobius verwenden, da die anderen optionale Erweiterungen des Kerns sind.
Das Testmodul enthält Dienstprogramme, die Ihnen beim Schreiben von Tests für Mobius-Anwendungen helfen. Es sollte nur als Testabhängigkeit verwendet werden.
Die RX-Module enthalten Erweiterungen für RxJava. Sie sollten eine davon in Ihren Mobius-Anwendungen verwenden, da sie die Erstellung von Effekthandlern und Ereignisquellen vereinfachen. Beide RxJava-Module nutzen die gleiche API, der einzige Unterschied besteht darin, dass eines für RxJava 1.x und das andere für RxJava 2.x erstellt wurde.
Das Android-Modul enthält hauptsächlich Klassen zum Anschließen eines MobiusLoop an Android.
Das Extras-Modul enthält Dienstprogramme und Klassen, die dabei helfen, den Boilerplate für einige erweiterte Nutzungsmuster (z. B. verschachtelte Update-Funktionen) zu reduzieren.
Das Ziel von Mobius ist es, Ihnen eine bessere Kontrolle über den Status Ihrer Bewerbung zu geben. Sie können sich Ihren Zustand als eine Momentaufnahme aller aktuellen Werte der Variablen in Ihrer Anwendung vorstellen. In Mobius kapseln wir den gesamten Zustand in einer Datenstruktur, die wir Modell nennen.
Das Modell kann durch einen beliebigen Typ dargestellt werden. In diesem Beispiel erstellen wir einen einfachen Zähler, sodass unser gesamter Status in einem Integer
enthalten sein kann:
Mit Mobius können Sie den Status nicht direkt manipulieren. Um den Status zu ändern, müssen Sie dem Framework Nachrichten senden, in denen Sie angeben, was Sie tun möchten. Wir nennen diese Nachrichten Ereignisse . In unserem Fall möchten wir unseren Zähler erhöhen und verringern. Lassen Sie uns eine enum
verwenden, um diese Fälle zu definieren:
enum CounterEvent {
INCREMENT ,
DECREMENT ,
}
Nachdem wir nun ein Modell und einige Ereignisse haben, müssen wir Mobius eine Reihe von Regeln geben, mit denen es den Status in unserem Namen aktualisieren kann. Wir tun dies, indem wir dem Framework eine Funktion geben, die nacheinander mit jedem eingehenden Ereignis und dem neuesten Modell aufgerufen wird, um das nächste Modell zu generieren:
class CounterLogic {
static Integer update ( Integer model , CounterEvent event ) {
switch ( event ) {
case INCREMENT : return model + 1 ;
case DECREMENT : return model - 1 ;
}
}
}
Mit diesen Bausteinen können wir beginnen, unsere Anwendungen als Übergänge zwischen diskreten Zuständen als Reaktion auf Ereignisse zu betrachten. Aber wir glauben, dass noch ein Teil des Puzzles fehlt – nämlich die Nebenwirkungen, die mit dem Wechsel zwischen Staaten einhergehen. Wenn Sie beispielsweise auf die Schaltfläche „Aktualisieren“ klicken, wird unsere Anwendung möglicherweise in einen „Ladezustand“ versetzt, mit dem Nebeneffekt, dass auch die neuesten Daten von unserem Backend abgerufen werden.
In Mobius nennen wir diese Nebenwirkungen treffend „ Effekte “. Nehmen wir im Fall unseres Zählers an, dass wir stattdessen einen Soundeffekt abspielen, wenn der Benutzer versucht, unter 0 zu dekrementieren. Erstellen wir eine enum
, die alle möglichen Effekte darstellt (in diesem Fall ist es nur einer):
enum CounterEffect {
PLAY_SOUND ,
}
Jetzt müssen wir unsere update
erweitern, um auch eine Reihe von Effekten zurückzugeben, die mit bestimmten Zustandsübergängen verbunden sind. Dazu implementieren wir die Update
Schnittstelle wie folgt:
class CounterLogic implements Update < Integer , CounterEvent , CounterEffect > {
public Next < Integer , CounterEffect > update ( Integer model , CounterEvent event ) {
switch ( event ) {
case INCREMENT :
return next ( model + 1 );
case DECREMENT :
if ( model == 0 ) {
Set < CounterEffect > soundEffect = effects ( CounterEffect . PLAY_SOUND );
return dispatch ( soundEffect );
}
return next ( model - 1 );
}
throw new IllegalStateException ( "Unhandled event: " + event );
}
}
Mobius sendet jeden der Effekte, die Sie bei jedem Zustandsübergang zurückgeben, an einen sogenannten Effekthandler . Lassen Sie uns jetzt eines davon erstellen, indem wir die Connectable
-Schnittstelle implementieren:
class CounterEffectHandler implements Connectable < CounterEffect , CounterEvent > {
public Connection < CounterEffect > connect ( Consumer < CounterEvent > output ) {
return new Connection < CounterEffect >() {
@ Override
public void accept ( CounterEffect effect ) {
if ( effect == CounterEffect . PLAY_SOUND ) {
Toolkit . getDefaultToolkit (). beep ();
}
}
@ Override
public void dispose () {}
};
}
}
Nachdem wir nun alle Teile an ihrem Platz haben, lasst uns alles zusammenfügen:
public static void main ( String [] args ) {
// Let's make a Mobius Loop
MobiusLoop < Integer , CounterEvent , CounterEffect > loop = Mobius
. loop ( new CounterLogic (), new CounterEffectHandler ())
. startFrom ( 0 );
// And start using our loop
loop . dispatchEvent ( CounterEvent . INCREMENT ); // Model is now 1
loop . dispatchEvent ( CounterEvent . DECREMENT ); // Model is now 0
loop . dispatchEvent ( CounterEvent . DECREMENT ); // Sound effect plays! Model is still 0
}
Dies deckt die Grundlagen von Mobius ab. Um mehr zu erfahren, besuchen Sie unsere Website.
Wir verwenden den automatischen Formatierer von Google, um den Code zu formatieren. Die Build-Pipeline ist so eingerichtet, dass Builds fehlschlagen, die nicht korrekt formatiert sind. Um die korrekte Formatierung sicherzustellen, führen Sie Folgendes aus:
./gradlew format
Dieses Projekt folgt dem Open Code of Conduct. Durch Ihre Teilnahme wird von Ihnen erwartet, dass Sie diesen Kodex respektieren.