我用Jetpack Compose+Orbit MVI创建了一个神奇宝贝图书馆应用程序。Orbit MVI易于使用,请尝试。
链接
此示例源代码
关于Jetpack Compose
关于 Orbit MVI
特征
- 管理神奇宝贝数据。
- 在列表中显示多个神奇宝贝。
- 显示神奇宝贝的详细信息。
建筑风格
此应用程序架构基于 MVVM + Repository 模式。但是视图状态管理使用 MVI,因为引入了 Jetpack Compose 和 Orbit MVI。
模块
该应用程序由多模块结构组成。共有三个模块,每个模块的作用如下图所述。
姓名 | 内容 |
---|---|
应用程序 | 存储 View 和 ViewModel 以管理视图状态。 |
领域 | 存储 UseCase 以处理业务逻辑。 |
数据 | Store Repository 和 Dao 来管理 Pokémon 数据库。 |
图书馆
此应用程序由底部图中的库创建。
姓名 | 关联 |
---|---|
Koin | https://insert-koin.io |
Jetpack Compose | https://developer.android.com/jetpack/compose |
导航组合 | https://developer.android.com/jetpack/compose/navigation |
Orbit MVI | https://orbit-mvi.org |
Coil | https://coil-kt.github.io/coil/ |
Room | https://developer.android.com/training/data-storage/room?hl=ja |
Kotlin 序列化 | https://github.com/Kotlin/kotlinx.serialization |
口袋妖怪Pokedex | https://github.com/Biuni/PokemonGO-Pokedex |
UI 的数据流(UI 状态管理)
此应用程序数据流是无方向的,如下图所示。
- MVI View 将 Action(intent) 发送到 MVI Model。
- MVI 模型在收到 Action 时执行 UseCase
- UseCase 访问 Repository 以获取 Pokémon Data。
- UseCase 将 Pokémon 数据返回给 MVI 模型。
- MVI 模型从神奇宝贝数据创建新状态。
- MVI 模型将创建的新状态或新事件发送到 MVI 视图。
此应用程序数据流由 Orbit MVI 实现。(有关如何使用 Orbit MVI 创建 MVI 视图或 MVI 模型的更多信息,请查看此处。)
MVI模型
data class InitState(
val status: UiStatus? = null
)
sealed class InitSideEffect {
object Completed : InitSideEffect()
}
class InitViewModel(
private val fetchAllPokemonUseCase: FetchAllPokemonUseCase
) : ContainerHost<InitState, InitSideEffect>, ViewModel() {
override val container = container<InitState, InitSideEffect>(
InitState()
)
init {
fetchData()
}
fun retry() {
if (container.stateFlow.value.status != UiStatus.Loading) {
fetchData()
}
}
private fun fetchData() {
intent {
reduce { state.copy(status = UiStatus.Loading) }
if (fetchAllPokemonUseCase()) {
reduce { state.copy(status = UiStatus.Success) }
postSideEffect(InitSideEffect.Completed)
} else {
reduce { state.copy(status = UiStatus.Failed()) }
}
}
}
}
MVI 视图
@Composable
fun InitPage(
viewModel: InitViewModel,
onCompleted: () -> Unit
) {
val state by viewModel.container.stateFlow.collectAsState()
LaunchedEffect(viewModel) {
viewModel.container.sideEffectFlow.collect {
when (it) {
is InitSideEffect.Completed -> onCompleted()
}
}
}
Scaffold {
Box(modifier = Modifier.fillMaxSize()) {
when (state.status) {
UiStatus.Loading -> {
DownloadingMessage(
modifier = Modifier
.wrapContentSize()
.align(Alignment.Center)
)
}
is UiStatus.Failed -> {
DownloadRetryMessage(
onRetry = { viewModel.retry() },
modifier = Modifier
.wrapContentSize()
.align(Alignment.Center)
)
}
UiStatus.Success -> Unit
}
}
}
}
链接:https://dev.to/kaleidot725/implementaing-jetpack-compose-orbit-mvi-3gea