Mobius es un marco reactivo funcional para gestionar la evolución del estado y los efectos secundarios, con complementos para conectarse a las UI de Android y RxJava Observables. Enfatiza la separación de preocupaciones, la capacidad de prueba y el aislamiento de partes del código con estado.
Para obtener más información, visite el sitio web para obtener una guía del usuario. Para ver Mobius en acción, consulte la aplicación TODO de muestra basada en la aplicación de Android Architecture Blueprints. También puedes ver una charla de Android @Scale presentando Mobius.
Mobius está en estado de producción, lo que significa que se utiliza en producción en las aplicaciones Spotify de Android y consideramos que las API son estables y la implementación está libre de errores. No haremos cambios que rompan la compatibilidad con versiones anteriores.
Mobius está actualmente creado para Java 7 (porque Java 8 no es totalmente compatible con todas las versiones de Android), de ahí la duplicación de algunos conceptos definidos en java.util.function
(ver com.spotify.mobius.functions
).
Al usar Mobius, recomendamos usar Kotlin o Java 8 o posterior, principalmente debido a la inferencia de tipos mejorada y porque el uso de lambdas mejora en gran medida la legibilidad y la concisión del código.
La última versión de Mobius está disponible a través de Maven Central (LATEST_RELEASE a continuación es):
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
Este es el núcleo de Mobius, del que dependen todos los demás módulos. Es una biblioteca Java pura que es completamente autónoma. Este es el único módulo que necesitas cuando usas Mobius, porque los demás son extensiones opcionales del núcleo.
El módulo de prueba contiene utilidades que le ayudan a escribir pruebas para aplicaciones Mobius. Sólo debe usarse como dependencia de prueba.
Los módulos rx contienen extensiones para RxJava. Deberías usar uno de ellos en tus aplicaciones Mobius ya que simplifican la creación de controladores de efectos y fuentes de eventos. Ambos módulos RxJava comparten la misma API, la única diferencia es que uno está creado para RxJava 1.x y el otro para RxJava 2.x.
El módulo de Android contiene principalmente clases para conectar un MobiusLoop a Android.
El módulo de extras contiene utilidades y clases que ayudan a reducir el texto estándar para algunos patrones de uso más avanzados (por ejemplo, funciones de actualización anidadas).
El objetivo de Mobius es brindarle un mejor control sobre el estado de su aplicación. Puede pensar en su estado como una instantánea de todos los valores actuales de las variables en su aplicación. En Mobius, encapsulamos todo el estado en una estructura de datos que llamamos Modelo .
El modelo puede representarse del tipo que desee. En este ejemplo, construiremos un contador simple, de modo que todo nuestro estado pueda estar contenido en un Integer
:
Mobius no te permite manipular el estado directamente. Para cambiar el estado, debe enviar mensajes al marco que indiquen lo que desea hacer. A estos mensajes los llamamos Eventos . En nuestro caso, querremos incrementar y disminuir nuestro contador. Usemos una enum
para definir estos casos:
enum CounterEvent {
INCREMENT ,
DECREMENT ,
}
Ahora que tenemos un Modelo y algunos Eventos , necesitaremos darle a Mobius un conjunto de reglas que pueda usar para actualizar el estado en nuestro nombre. Hacemos esto dándole al marco una función que se llamará secuencialmente con cada evento entrante y el modelo más reciente, para generar el siguiente modelo :
class CounterLogic {
static Integer update ( Integer model , CounterEvent event ) {
switch ( event ) {
case INCREMENT : return model + 1 ;
case DECREMENT : return model - 1 ;
}
}
}
Con estos componentes básicos, podemos empezar a pensar en nuestras aplicaciones como transiciones entre estados discretos en respuesta a eventos. Pero creemos que todavía falta una pieza del rompecabezas: los efectos secundarios asociados con el movimiento entre estados. Por ejemplo, presionar un botón "actualizar" podría poner nuestra aplicación en un estado de "carga", con el efecto secundario de obtener también los datos más recientes de nuestro backend.
En Mobius, acertadamente llamamos a estos efectos secundarios Efectos . En el caso de nuestro contador, digamos que cuando el usuario intenta disminuir por debajo de 0, reproducimos un efecto de sonido. Creemos una enum
que represente todos los efectos posibles (que en este caso es solo uno):
enum CounterEffect {
PLAY_SOUND ,
}
Ahora necesitaremos aumentar nuestra función update
para devolver también un conjunto de efectos asociados con ciertas transiciones de estado. Para hacer esto implementaremos la interfaz Update
de esta manera:
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 envía cada uno de los efectos que devuelve en cualquier transición de estado a algo llamado Effect Handler . Hagamos uno de esos ahora implementando la interfaz Connectable
:
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 () {}
};
}
}
Ahora que tenemos todas las piezas en su lugar, unámoslo todo:
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
}
Esto cubre los fundamentos de Mobius. Para obtener más información, visite nuestro sitio web.
Estamos utilizando el formateador automático de Google para formatear el código. La canalización de compilación está configurada para fallar en compilaciones que no están formateadas correctamente. Para garantizar el formato correcto, ejecute
./gradlew format
Este proyecto se adhiere al Código Abierto de Conducta. Al participar, se espera que respete este código.