2024年已经过半,作为一名聋人独立开发者,我常常反思这半年的进步和收获。在这篇文章中,我分享如何使用 Jetpack Compose、Material3 结合 MVI(Model-View-Intent) 架构设计一个模块化的Android应用。无论你是新手开发者,还是有经验的安卓开发人员,相信这篇文章都会对你有所帮助。
一、什么是 MVI 架构?
MVI 是 Model-View-Intent 的缩写,MVI 架构主要有三个核心部分:
- Model (模型):数据层,用于表示应用的状态,可以是从数据库、网络请求等获取的数据。
- View (视图):用户界面层,展示应用的 UI,响应用户的交互。
- Intent (意图):用户的交互事件或系统触发的操作,会导致 Model 的变化,最终反映到 View 上。
二、模块化架构设计
项目划分为以下几个模块:
- UI 层:负责界面渲染和用户交互逻辑。
- 数据层:管理数据获取、存储、处理(包括与网络和数据库的交互)。
- 业务逻辑层(ViewModel/Intent):连接 UI 和数据层,处理业务逻辑和状态管理。
采用的架构是 MVI(Model-View-Intent),在应用中的状态是不可变的,数据流是单向的,让 UI 的变化是可预测的。
三、项目结构
项目基于模块化设计,按照以下目录划分:
代码语言:java复制com.nim.mviapp/
├── data/ # 数据层
│ ├── repository/ # 数据仓库逻辑
│ ├── model/ # 数据模型
│ └── database/ # Room数据库
│
├── ui/ # UI 层
│ ├── theme/ # Material3 主题
│ ├── component/ # 可复用的 UI 组件
│ └── screen/ # 页面定义,如HomeScreen等
│
├── viewmodel/ # 业务逻辑层 (Intent ViewModel)
│ └── WishViewModel.kt # 处理业务逻辑及状态管理
│
└── MainApplication.kt # 应用入口
四、模块功能解析
4.1 数据层
数据层采用 Repository Pattern,统一管理数据的来源。通过 Room 进行本地存储,使用 Flow 处理数据流,为了方便是 MVI 中的单向数据流保持一样。
代码语言:java复制class WishRepository(private val wishDao: WishDao) {
fun getWishes(): Flow<List<Wish>> = wishDao.getAllWishes()
suspend fun addWish(wish: Wish) {
wishDao.addAWish(wish)
}
suspend fun updateWishLikedStatus(wish: Wish, isLiked: Boolean) {
wish.isLiked = isLiked
wishDao.updateWish(wish)
}
// 其他CRUD操作...
}
4.2 业务逻辑层 (Intent ViewModel)
在 MVI 中,用户的每个操作都会包装成 Intent,然后通过 ViewModel 处理。ViewModel 管理数据层的交互,还负责保持 UI 状态的同步。
代码语言:java复制class WishViewModel(private val repository: WishRepository) : ViewModel() {
val wishes: LiveData<List<Wish>> = repository.getWishes().asLiveData()
fun addWish(wish: Wish) {
viewModelScope.launch {
repository.addWish(wish)
}
}
// 新增的点赞状态更新方法
fun updateWishLikedStatus(wish: Wish, isLiked: Boolean) {
viewModelScope.launch {
repository.updateWishLikedStatus(wish, isLiked)
}
}
// 其他Intent的处理逻辑...
}
每个 Intent 都会触发相应的状态更新。
4.3 UI 层
UI 层通过 Jetpack Compose 和 Material3 构建应用 UI。借助 Compose 声明式的设计模式,可以轻松创建可复用的组件,结合 MVI 确保状态变化时界面自动更新。
代码语言:java复制@Composable
fun HomeScreen(viewModel: WishViewModel) {
val wishes by viewModel.wishes.observeAsState(initial = emptyList())
LazyColumn {
items(wishes) { wish ->
Text(text = wish.title)
}
}
}
View 始终通过 Intent 和 ViewModel 交互,UI 渲染通过不可变的状态进行。
五、页面导航的实现
Jetpack Compose 提供了内置的导航库,帮助我们管理应用的页面跳转。页面之间的导航逻辑放在 MainScreen 中:
代码语言:java复制@Composable
fun MainScreen() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("detail/{id}") { backStackEntry ->
val id = backStackEntry.arguments?.getString("id")
DetailScreen(navController, id)
}
}
}
这样设计可以实现页面的解耦,减少依赖。
六、状态管理与数据流
状态是不可变的,每个操作都会产生一个新的状态。以下是一个状态驱动 UI 更新的例子:
代码语言:java复制@Composable
fun WishItem(viewModel: WishViewModel, wish: Wish) {
var isLiked by remember { mutableStateOf(false) }
Column {
Text(text = wish.title)
IconButton(onClick = {
isLiked = !isLiked
viewModel.updateWishLikedStatus(wish, isLiked) // 调用 ViewModel 方法更新点赞状态
}) {
Icon(imageVector = Icons.Default.Favorite, tint = if (isLiked) Color.Red else Color.Gray)
}
}
}
每当用户点击图标,状态 isLiked 发生变化,UI 会根据新的状态自动重绘。
七、总结
这种模块化设计架构极大地提升了应用的可维护性和扩展性。对于复杂项目而言,采用 MVI 这种单向数据流的架构设计可以减少状态管理的混乱,确保每一次状态变化都是可预测且可控的。
有任何问题欢迎提问,感谢大家阅读 )