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
该项目遵守开放行为准则。通过参与,您应该遵守此准则。