Mobius — это функциональная реактивная платформа для управления развитием состояния и побочными эффектами с надстройками для подключения к пользовательским интерфейсам Android и RxJava Observables. Он подчеркивает разделение задач, тестируемость и изоляцию частей кода с состоянием.
Чтобы узнать больше, посетите веб-сайт с руководством пользователя. Чтобы увидеть Mobius в действии, посмотрите пример приложения TODO, основанного на приложении из Android Architecture Blueprints. Вы также можете посмотреть выступление Android @Scale, рассказывающее о Mobius.
Mobius находится в статусе «Производство», что означает, что он используется в производстве в приложениях Spotify для Android, и мы считаем, что API-интерфейсы стабильны, а реализация не содержит ошибок. Мы не будем вносить изменения, нарушающие обратную совместимость.
Mobius в настоящее время создан для Java 7 (поскольку Java 8 не полностью поддерживается во всех версиях Android), поэтому некоторые концепции, определенные в java.util.function
(см. com.spotify.mobius.functions
), дублируются.
При использовании Mobius мы рекомендуем использовать Kotlin или Java 8 или более позднюю версию, в первую очередь из-за улучшенного вывода типов и потому, что использование лямбда-выражений значительно улучшает читаемость и краткость кода.
Последняя версия Mobius доступна через Maven Central (LATEST_RELEASE ниже):
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
Это ядро Мобиуса, от которого зависят все остальные модули. Это чистая Java-библиотека, полностью автономная. Это единственный модуль, который вам понадобится при использовании Mobius, поскольку остальные являются дополнительными расширениями ядра.
Тестовый модуль содержит утилиты, помогающие писать тесты для приложений Mobius. Его следует использовать только в качестве тестовой зависимости.
Модули rx содержат расширения для RxJava. Вам следует использовать один из них в своих приложениях Mobius, поскольку они упрощают создание обработчиков эффектов и источников событий. Оба модуля RxJava используют один и тот же API, с той лишь разницей, что один создан для RxJava 1.x, а другой — для RxJava 2.x.
Модуль android в основном содержит классы для подключения MobiusLoop к Android.
Модуль дополнительных функций содержит утилиты и классы, которые помогают сократить шаблонный набор шаблонов для некоторых более сложных шаблонов использования (например, вложенных функций обновления).
Цель Mobius — дать вам лучший контроль над состоянием вашего приложения. Вы можете думать о своем состоянии как о снимке всех текущих значений переменных в вашем приложении. В Mobius мы инкапсулируем все состояние в структуру данных, которую мы называем Моделью .
Модель может быть представлена любым типом, который вам нравится. В этом примере мы создадим простой счетчик, чтобы все наше состояние могло содержаться в Integer
:
Mobius не позволяет напрямую манипулировать состоянием. Чтобы изменить состояние, вам нужно отправить фреймворку сообщения о том, что вы хотите сделать. Мы называем эти сообщения Событиями . В нашем случае мы захотим увеличивать и уменьшать наш счетчик. Давайте воспользуемся enum
для определения этих случаев:
enum CounterEvent {
INCREMENT ,
DECREMENT ,
}
Теперь, когда у нас есть Model и некоторые Event , нам нужно предоставить Mobius набор правил, которые он сможет использовать для обновления состояния от нашего имени. Мы делаем это, предоставляя платформе функцию, которая будет последовательно вызываться при каждом входящем событии и самой последней модели , чтобы сгенерировать следующую модель :
class CounterLogic {
static Integer update ( Integer model , CounterEvent event ) {
switch ( event ) {
case INCREMENT : return model + 1 ;
case DECREMENT : return model - 1 ;
}
}
}
Используя эти строительные блоки, мы можем начать думать о наших приложениях как о переходах между дискретными состояниями в ответ на события. Но мы считаем, что в головоломке все еще не хватает одной детали, а именно побочных эффектов, связанных с перемещением между состояниями. Например, нажатие кнопки «обновить» может перевести наше приложение в состояние «загрузки» с побочным эффектом получения последних данных из нашего бэкэнда.
В Mobius мы метко называем эти побочные эффекты Эффектами . В случае нашего счетчика скажем, что когда пользователь пытается уменьшить значение ниже 0, вместо этого мы воспроизводим звуковой эффект. Давайте создадим enum
, которое будет представлять все возможные эффекты (в данном случае только один):
enum CounterEffect {
PLAY_SOUND ,
}
Теперь нам нужно расширить нашу функцию update
, чтобы она также возвращала набор эффектов, связанных с определенными переходами состояний. Для этого мы реализуем интерфейс Update
следующим образом:
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 отправляет каждый из эффектов, которые вы возвращаете при любом переходе состояния, в так называемый обработчик эффектов . Давайте сделаем один из них, реализовав интерфейс 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 () {}
};
}
}
Теперь, когда у нас есть все детали, давайте свяжем их воедино:
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
}
Здесь рассматриваются основы Mobius. Чтобы узнать больше, зайдите на наш сайт.
Для форматирования кода мы используем автоформатер Google. Конвейер сборки настроен на сбой при сборке, которая отформатирована неправильно. Чтобы убедиться в правильности форматирования, запустите
./gradlew format
Этот проект придерживается Открытого кодекса поведения. Принимая участие, вы должны соблюдать этот кодекс.