Mobius é uma estrutura reativa funcional para gerenciar a evolução do estado e efeitos colaterais, com complementos para conexão com UIs Android e RxJava Observables. Ele enfatiza a separação de preocupações, a testabilidade e o isolamento de partes com estado do código.
Para saber mais, visite o site para obter um guia do usuário. Para ver o Mobius em ação, confira o exemplo do aplicativo TODO baseado no aplicativo Android Architecture Blueprints. Você também pode assistir a uma palestra do Android @Scale apresentando o Mobius.
Mobius está em status de produção, o que significa que é usado em produção em aplicativos Spotify Android, e que consideramos as APIs estáveis e a implementação livre de bugs. Não faremos alterações que quebrem a compatibilidade com versões anteriores.
Mobius é atualmente construído para Java 7 (porque Java 8 não é totalmente suportado em todas as versões do Android), daí a duplicação de alguns conceitos definidos em java.util.function
(veja com.spotify.mobius.functions
).
Ao usar Mobius, recomendamos usar Kotlin ou Java 8 ou posterior, principalmente por causa da inferência de tipo aprimorada e porque o uso de lambdas melhora muito a legibilidade e a concisão do código.
A versão mais recente do Mobius está disponível no Maven Central (LATEST_RELEASE abaixo):
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 é o núcleo do Mobius, do qual todos os outros módulos dependem. É uma biblioteca Java pura e totalmente independente. Este é o único módulo que você precisa ao usar o Mobius, pois os outros são extensões opcionais do núcleo.
O módulo de teste contém utilitários que ajudam a escrever testes para aplicativos Mobius. Deve ser usado apenas como uma dependência de teste.
Os módulos rx contêm extensões para RxJava. Você deve usar um deles em seus aplicativos Mobius, pois eles simplificam a criação de manipuladores de efeitos e fontes de eventos. Ambos os módulos RxJava compartilham a mesma API, a única diferença é que um é construído para RxJava 1.xe o outro para RxJava 2.x.
O módulo Android contém principalmente classes para conectar um MobiusLoop ao Android.
O módulo extras contém utilitários e classes que ajudam a reduzir o padrão para alguns padrões de uso mais avançados (por exemplo, funções de atualização aninhadas).
O objetivo do Mobius é fornecer melhor controle sobre o estado do seu aplicativo. Você pode pensar no seu estado como um instantâneo de todos os valores atuais das variáveis na sua aplicação. No Mobius, encapsulamos todo o estado em uma estrutura de dados que chamamos de Model .
O Modelo pode ser representado pelo tipo que você desejar. Neste exemplo estaremos construindo um contador simples, para que todo o nosso estado possa estar contido em um Integer
:
Mobius não permite manipular o estado diretamente. Para alterar o estado, você deve enviar mensagens ao framework dizendo o que deseja fazer. Chamamos essas mensagens de Eventos . No nosso caso, desejaremos aumentar e diminuir nosso contador. Vamos usar um enum
para definir esses casos:
enum CounterEvent {
INCREMENT ,
DECREMENT ,
}
Agora que temos um Model e alguns Event s, precisaremos dar ao Mobius um conjunto de regras que ele pode usar para atualizar o estado em nosso nome. Fazemos isso dando ao framework uma função que será chamada sequencialmente com cada Evento recebido e o Model mais recente, para gerar o próximo Model :
class CounterLogic {
static Integer update ( Integer model , CounterEvent event ) {
switch ( event ) {
case INCREMENT : return model + 1 ;
case DECREMENT : return model - 1 ;
}
}
}
Com esses blocos de construção, podemos começar a pensar em nossas aplicações como transições entre estados discretos em resposta a eventos. Mas acreditamos que ainda falta uma peça no puzzle – nomeadamente os efeitos secundários associados à movimentação entre Estados. Por exemplo, pressionar um botão "atualizar" pode colocar nosso aplicativo em um estado de "carregamento", com o efeito colateral de também buscar os dados mais recentes de nosso back-end.
Em Mobius, chamamos apropriadamente esses efeitos colaterais de Effect s. No caso do nosso contador, digamos que quando o usuário tenta diminuir abaixo de 0, em vez disso tocamos um efeito sonoro. Vamos criar um enum
que represente todos os efeitos possíveis (que neste caso é apenas um):
enum CounterEffect {
PLAY_SOUND ,
}
Agora precisaremos aumentar nossa função update
para retornar também um conjunto de efeitos associados a certas transições de estado. Para fazer isso, implementaremos a interface Update
da seguinte forma:
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 envia cada um dos efeitos que você retorna em qualquer transição de estado para algo chamado Effect Handler . Vamos fazer um desses agora implementando a interface 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 () {}
};
}
}
Agora que temos todas as peças no lugar, vamos unir tudo:
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
}
Isso cobre os fundamentos do Mobius. Para saber mais, acesse nosso site.
Estamos usando o formatador automático do Google para formatar o código. O pipeline de build está configurado para falhar em builds que não estejam formatados corretamente. Para garantir a formatação correta, execute
./gradlew format
Este projeto segue o Código de Conduta Aberto. Ao participar, espera-se que você honre este código.