Mobius 是一個用於管理狀態演化和副作用的功能反應式框架,具有用於連接 Android UI 和 RxJava Observables 的附加元件。它強調關注點分離、可測試性以及隔離程式碼的有狀態部分。
要了解更多信息,請訪問網站獲取用戶指南。若要查看 Mobius 的實際效果,請查看基於 Android 架構藍圖中的應用程式的範例 TODO 應用程式。您也可以觀看 Android @Scale 介紹 Mobius 的演講。
Mobius 處於生產狀態,這意味著它在 Spotify Android 應用程式的生產中使用,並且我們認為 API 是穩定的且實作沒有錯誤。我們不會做出破壞向後相容性的變更。
Mobius 目前是為 Java 7 建構的(因為並非所有版本的 Android 都完全支援 Java 8),因此重複了java.util.function
中定義的一些概念(請參閱com.spotify.mobius.functions
)。
使用 Mobius 時,我們建議使用 Kotlin 或 Java 8 或更高版本,主要是因為改進了類型推斷,並且使用 lambda 大大提高了程式碼的可讀性和簡潔性。
最新版本的 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 的類別。
extras 模組包含實用程式和類,有助於減少一些更高級使用模式(例如,嵌套更新函數)的樣板檔案。
Mobius 的目標是讓您更好地控制應用程式狀態。您可以將您的狀態視為應用程式中變數的所有目前值的快照。在莫比烏斯中,我們將所有狀態封裝在一個資料結構中,我們稱之為模型。
模型可以用您喜歡的任何類型來表示。在此範例中,我們將建立一個簡單的計數器,因此所有狀態都可以包含在Integer
中:
莫比烏斯不允許你直接操縱狀態。為了變更狀態,您必須向框架發送訊息說明您想要做什麼。我們將這些訊息稱為「事件」 。在我們的例子中,我們需要增加和減少計數器。讓我們使用enum
來定義這些情況:
enum CounterEvent {
INCREMENT ,
DECREMENT ,
}
現在我們有了一個Model和一些Event ,我們需要為 Mobius 提供一組規則,它可以用來代表我們更新狀態。我們透過為框架提供一個函數來實現這一點,該函數將在每個傳入的Event和最新的Model中順序調用,以產生下一個Model :
class CounterLogic {
static Integer update ( Integer model , CounterEvent event ) {
switch ( event ) {
case INCREMENT : return model + 1 ;
case DECREMENT : return model - 1 ;
}
}
}
有了這些建構塊,我們就可以開始將我們的應用程式視為回應事件的離散狀態之間的轉換。但我們認為,這個難題仍然缺少一個部分──也就是與狀態之間移動相關的副作用。例如,按下「刷新」按鈕可能會使我們的應用程式進入「載入」狀態,其副作用是也會從後端取得最新資料。
在莫比烏斯中,我們恰當地將這些副作用稱為Effect 。就我們的計數器而言,假設當使用者嘗試減少到 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
}
這涵蓋了莫比烏斯的基本原理。要了解更多信息,請訪問我們的網站。
我們使用 Google 的自動格式化程式來格式化程式碼。建置管道設定為使格式不正確的建置失敗。為了確保格式正確,請運行
./gradlew format
該項目遵守開放行為準則。透過參與,您應該遵守此準則。