Mobius は、状態の進化と副作用を管理するための機能的なリアクティブ フレームワークであり、Android UI および RxJava Observable に接続するためのアドオンを備えています。これは、関心事の分離、テストのしやすさ、コードのステートフルな部分の分離を重視しています。
詳細については、Web サイトのユーザー ガイドをご覧ください。 Mobius の動作を確認するには、Android アーキテクチャ ブループリントのアプリに基づくサンプル TODO アプリをチェックしてください。 Mobius を紹介する Android @Scale の講演もご覧いただけます。
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
これは Mobius のコアであり、他のすべてのモジュールが依存します。これは完全に自己完結型の純粋な Java ライブラリです。他のモジュールはコアに対するオプションの拡張機能であるため、これは Mobius を使用するときに必要な唯一のモジュールです。
テスト モジュールには、Mobius アプリケーションのテストの作成に役立つユーティリティが含まれています。これはテストの依存関係としてのみ使用してください。
rx モジュールには、RxJava の拡張機能が含まれています。これらのいずれかを使用すると、エフェクト ハンドラーとイベント ソースの作成が簡単になるため、Mobius アプリケーションでこれらのいずれかを使用する必要があります。どちらの RxJava モジュールも同じ API を共有しますが、唯一の違いは、1 つは RxJava 1.x 用にビルドされ、もう 1 つは RxJava 2.x 用にビルドされていることです。
Android モジュールには主に、MobiusLoop を Android に接続するためのクラスが含まれています。
extras モジュールには、より高度な使用パターン (ネストされた更新関数など) の定型文を減らすのに役立つユーティリティとクラスが含まれています。
Mobius の目標は、アプリケーションの状態をより適切に制御できるようにすることです。状態は、アプリケーション内の変数の現在のすべての値のスナップショットと考えることができます。 Mobius では、すべての状態をModelと呼ぶデータ構造にカプセル化します。
モデルは任意のタイプで表すことができます。この例では、すべての状態をInteger
に含めることができるように、単純なカウンターを構築します。
Mobius では状態を直接操作することはできません。状態を変更するには、何をしたいのかを示すフレームワーク メッセージを送信する必要があります。これらのメッセージをイベント と呼びます。私たちの場合、カウンタを増減させたいと考えます。 enum
使用してこれらのケースを定義しましょう。
enum CounterEvent {
INCREMENT ,
DECREMENT ,
}
ModelといくつかのEventができたので、Mobius に代わって状態を更新するために使用できる一連のルールを与える必要があります。これを行うには、次のModel を生成するために、すべての受信Eventと最新のModelで順次呼び出される関数をフレームワークに与えます。
class CounterLogic {
static Integer update ( Integer model , CounterEvent event ) {
switch ( event ) {
case INCREMENT : return model + 1 ;
case DECREMENT : return model - 1 ;
}
}
}
これらの構成要素を使用すると、アプリケーションをイベントに応じた離散状態間の遷移として考えることができます。しかし、パズルにはまだ 1 つのピースが欠けていると私たちは考えています。それは、状態間の移動に伴う副作用です。たとえば、「更新」ボタンを押すと、アプリケーションが「読み込み中」状態になり、副作用としてバックエンドから最新のデータもフェッチされる可能性があります。
Mobius では、これらの副作用を適切にEffectと呼びます。このカウンターの場合、ユーザーが 0 よりも減らそうとしたときに、代わりに効果音を再生するとします。考えられるすべての効果 (この場合は 1 つだけ) を表す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 は、状態遷移で返される各エフェクトをEffect Handlerと呼ばれるものに送信します。 Connectable
インターフェイスを実装して、これらの 1 つを作成しましょう。
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 の基本について説明します。さらに詳しく知りたい場合は、当社の Web サイトにアクセスしてください。
コードのフォーマットには Google のオートフォーマッタを使用しています。ビルド パイプラインは、正しくフォーマットされていないビルドは失敗するように設定されています。正しい形式であることを確認するには、次のコマンドを実行します。
./gradlew format
このプロジェクトはオープン行動規範に準拠しています。参加することにより、この規範を遵守することが期待されます。