Android Jetpack Compose + MVVM 开发流程深度分析

核心组件关系图
[View] -- 观察 --> [ViewModel] -- 操作 --> [Repository]|                              |
Compose UI                    StateFlow/LiveData|                              |
用户交互事件                       Room/Retrofit|                              |
[ViewModel] <-- 数据更新 -- [Data Sources]
开发流程详解
  1. 数据层 (Model)

    • 实体类定义:Room 实体或数据类
    • Repository 模式:
      • 统一管理数据源(Room/Retrofit/文件)
      • 提供干净的 API 给 ViewModel
  2. ViewModel 层

    • 使用 androidx.lifecycle 组件
    • 核心职责:
      • 持有 UI 状态(StateFlow/MutableStateFlow
      • 处理业务逻辑
      • 暴露不可变状态给 UI 层
    • 使用协程管理异步操作
  3. UI 层 (Compose)

    • 基于状态的声明式编程
    • 关键组件:
      • @Composable 函数构建 UI
      • remember 管理组件状态
      • ViewModel 通过 viewModel() 获取
    • 状态管理:
      • 通过 collectAsState() 观察 ViewModel 状态
      • 用户事件通过 ViewModel 方法回调
  4. 数据绑定

    • 单向数据流:
      用户操作
      调用 ViewModel 方法
      更新 Model 层
      ViewModel 更新 State
      Compose 自动重组
  5. 依赖管理

    • 使用 Hilt 实现依赖注入:
      • @HiltViewModel 注解 ViewModel
      • @Inject 构造函数注入 Repository

Todo 应用最佳实践实现

1. 添加依赖 (app/build.gradle)
dependencies {// Composeimplementation 'androidx.activity:activity-compose:1.8.0'implementation "androidx.compose.material3:material3:1.1.2"implementation "androidx.compose.ui:ui-tooling-preview:1.5.4"debugImplementation "androidx.compose.ui:ui-tooling:1.5.4"// Lifecycleimplementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2"implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.2"// Roomimplementation "androidx.room:room-runtime:2.6.0"implementation "androidx.room:room-ktx:2.6.0"kapt "androidx.room:room-compiler:2.6.0"// Hiltimplementation "com.google.dagger:hilt-android:2.48"kapt "com.google.dagger:hilt-compiler:2.48"
}
2. 数据模型
@Entity(tableName = "todos")
data class Todo(@PrimaryKey(autoGenerate = true) val id: Long = 0,val title: String,val description: String = "",val isCompleted: Boolean = false,val createdAt: LocalDateTime = LocalDateTime.now()
)
3. Repository 实现
interface TodoRepository {fun getAllTodos(): Flow<List<Todo>>suspend fun addTodo(todo: Todo)suspend fun updateTodo(todo: Todo)suspend fun deleteTodo(todo: Todo)
}class TodoRepositoryImpl @Inject constructor(private val todoDao: TodoDao
) : TodoRepository {override fun getAllTodos(): Flow<List<Todo>> = todoDao.getAll().map { list -> list.sortedByDescending { it.createdAt } }override suspend fun addTodo(todo: Todo) = todoDao.insert(todo)override suspend fun updateTodo(todo: Todo) = todoDao.update(todo)override suspend fun deleteTodo(todo: Todo) = todoDao.delete(todo)
}
4. ViewModel 实现
@HiltViewModel
class TodoViewModel @Inject constructor(private val repository: TodoRepository
) : ViewModel() {// UI 状态封装data class TodoUiState(val todos: List<Todo> = emptyList(),val isLoading: Boolean = false,val error: String? = null)// 使用 MutableStateFlow 管理状态private val _uiState = MutableStateFlow(TodoUiState(isLoading = true))val uiState: StateFlow<TodoUiState> = _uiState.asStateFlow()init {loadTodos()}private fun loadTodos() {viewModelScope.launch {repository.getAllTodos().catch { e -> _uiState.value = TodoUiState(error = e.message)}.collect { todos ->_uiState.value = TodoUiState(todos = todos)}}}fun addTodo(title: String, description: String = "") {viewModelScope.launch {val newTodo = Todo(title = title, description = description)repository.addTodo(newTodo)}}fun toggleTodoCompletion(todo: Todo) {viewModelScope.launch {repository.updateTodo(todo.copy(isCompleted = !todo.isCompleted))}}fun deleteTodo(todo: Todo) {viewModelScope.launch {repository.deleteTodo(todo)}}
}
5. Compose UI 实现
@Composable
fun TodoScreen(viewModel: TodoViewModel = viewModel()
) {val uiState by viewModel.uiState.collectAsState()var showDialog by remember { mutableStateOf(false) }var newTodoTitle by remember { mutableStateOf("") }Scaffold(topBar = { TopAppBar(title = { Text("Todo List") }) },floatingActionButton = {FloatingActionButton(onClick = { showDialog = true }) {Icon(Icons.Filled.Add, "Add Todo")}}) { padding ->when {uiState.isLoading -> Center { CircularProgressIndicator() }uiState.error != null -> Center { Text("Error: ${uiState.error}") }else -> TodoList(todos = uiState.todos,onToggleComplete = viewModel::toggleTodoCompletion,onDelete = viewModel::deleteTodo,modifier = Modifier.padding(padding))}}if (showDialog) {AlertDialog(onDismissRequest = { showDialog = false },title = { Text("Add New Todo") },text = {TextField(value = newTodoTitle,onValueChange = { newTodoTitle = it },label = { Text("Title") })},confirmButton = {Button(onClick = {viewModel.addTodo(newTodoTitle)newTodoTitle = ""showDialog = false},enabled = newTodoTitle.isNotBlank()) {Text("Add")}},dismissButton = {Button(onClick = { showDialog = false }) {Text("Cancel")}})}
}@Composable
fun TodoList(todos: List<Todo>,onToggleComplete: (Todo) -> Unit,onDelete: (Todo) -> Unit,modifier: Modifier = Modifier
) {LazyColumn(modifier = modifier) {items(items = todos,key = { it.id }) { todo ->TodoItem(todo = todo,onCheckedChange = { onToggleComplete(todo) },onDelete = { onDelete(todo) })}}
}@Composable
fun TodoItem(todo: Todo,onCheckedChange: (Boolean) -> Unit,onDelete: () -> Unit
) {Card(modifier = Modifier.fillMaxWidth().padding(8.dp),elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)) {Row(modifier = Modifier.padding(16.dp),verticalAlignment = Alignment.CenterVertically) {Checkbox(checked = todo.isCompleted,onCheckedChange = onCheckedChange)Column(modifier = Modifier.weight(1f)) {Text(text = todo.title,style = MaterialTheme.typography.titleMedium,textDecoration = if (todo.isCompleted) TextDecoration.LineThrough else null)if (todo.description.isNotBlank()) {Text(text = todo.description,style = MaterialTheme.typography.bodySmall)}}IconButton(onClick = onDelete) {Icon(Icons.Filled.Delete, "Delete")}}}
}
6. 数据库配置 (Room)
@Database(entities = [Todo::class], version = 1)
abstract class AppDatabase : RoomDatabase() {abstract fun todoDao(): TodoDao
}@Dao
interface TodoDao {@Query("SELECT * FROM todos")fun getAll(): Flow<List<Todo>>@Insertsuspend fun insert(todo: Todo)@Updatesuspend fun update(todo: Todo)@Deletesuspend fun delete(todo: Todo)
}

最佳实践要点

  1. 状态管理

    • 使用密封类管理 UI 状态(Loading/Success/Error)
    • ViewModel 暴露不可变 StateFlow,Compose 通过 collectAsState() 观察
    • UI 组件保持无状态,通过参数接收数据
  2. 性能优化

    • 使用 remember 缓存计算结果
    • 为 LazyColumn 的 items 设置唯一 key
    • 使用 derivedStateOf 处理复杂状态转换
  3. 架构分层

    调用
    调用
    本地数据
    远程数据
    UI Layer
    ViewModel
    Repository
    Room
    Retrofit
  4. 测试策略

    • ViewModel 测试:使用 TestCoroutineDispatcher
    • Compose UI 测试:使用 createComposeRule
    • Repository 测试:Mock 数据源
  5. 用户交互优化

    • 添加 Undo 删除功能(使用 Snackbar)
    • 实现本地搜索/过滤
    • 添加拖拽排序支持
  6. 错误处理

    • 在 Repository 捕获数据源异常
    • ViewModel 将异常转换为用户友好消息
    • UI 层显示错误状态并提供重试选项

此实现遵循了 Android 官方架构指南,结合了 Compose 的声明式特性和 MVVM 的响应式数据管理,提供了可测试、可维护的现代化 Android 应用架构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/915854.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/915854.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Tailwind CSS快速上手 Tailwind CSS的安装、配置、使用

&#x1f4da;前言 在Web前端开发的历史长河中&#xff0c;CSS的编写方式经历了多次演进&#xff0c;从早期的原生CSS 到 CSS预处理(Less/Sass/Stylus) 到 CSS-in-JS(Styled-Components/Emotion) 再到 Utility-First 原子化CSS。每一种演进方案其本质都是围绕“开发效率”、“…

单例模式的智慧:从UVM看控制的艺术

有时候&#xff0c;生活中的很多东西其实只需要一个就够了&#xff0c;就像一个公司只需要一个CEO&#xff0c;一个王朝只需要一个皇帝。在UVM验证环境中&#xff0c;也有很多这样的需求——有些对象&#xff0c;我们希望它在整个仿真过程中只存在一个实例。这就是我们今天要聊…

Hexo - 免费搭建个人博客01 - 安装软件工具

导言我的博客&#xff1a;https://q164129345.github.io/ Hexo 作为一个 Node.js 框架&#xff0c;它依赖于 Node.js 运行时环境来执行。 一、安装Node.js官方网址&#xff1a;https://nodejs.org/zh-cn追求系统稳定性、可靠性以及希望减少维护频率的用户来说&#xff0c;LTS版…

【Kubernetes】集群启动nginx,观察端口映射,work节点使用kubectl配置

参考b站叩丁狼总结&#xff1a;完整版Kubernetes&#xff08;K8S&#xff09;全套入门微服务实战项目&#xff0c;带你一站式深入掌握K8S核心能力 在master节点执行 kubectl create deployment nginx --imagenginxkubectl expose deployment nginx --port80 --typeNodePort1. …

20250704-基于强化学习在云计算环境中的虚拟机资源调度研究

基于强化学习在云计算环境中的虚拟机资源调度研究 随着云计算规模的持续扩大&#xff0c;数据中心虚拟机资源调度面临动态负载、异构资源适配及多目标优化等挑战。传统启发式算法在复杂场景下易陷入局部最优&#xff0c;而深度强化学习&#xff08;DRL&#xff09;凭借序贯决策…

day 33打卡

day 21 常见的降维算法 # 先运行之前预处理好的代码 import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import warnings warnings.filterwarnings(ignore)# 设置中文字体 plt.rcParams[font.sans-serif] [SimHei] plt.rcParam…

sec(x)积分推导

在MATLAB中绘制 sec⁡(x)、cos(x) 和 ln⁡∣sec⁡(x)tan⁡(x)∣的函数图像&#xff0c;需要特别注意 sec⁡(x) 在 cos⁡(x)0&#xff08;即 xπ/2kπ&#xff09;处的奇点。&#xff08;deepseek生成代码&#xff09;% 定义x范围&#xff08;-2π到2π&#xff09;&#xff0c;…

gpt面试题

vue面试题 &#x1f4a1; 一、响应式系统相关 ❓1. Vue 3 的响应式系统是如何实现的&#xff1f;和 Vue 2 有何本质区别&#xff1f; 答案&#xff1a; Vue 3 使用 Proxy 实现响应式&#xff08;位于 vue/reactivity 模块&#xff09;&#xff0c;替代 Vue 2 的 Object.defineP…

【基于OpenCV的图像处理】图像预处理之图像色彩空间转换以及图像灰度化处理

目录 零、写在前面的话 一、图像色彩空间转换 1.1 RGB颜色空间 1.1.1 RGB颜色空间概念 1.1.2 RGB颜色模型​编辑 1.1.3 关于颜色加法 1.1.4 颜色加权加法 1.2 HSV颜色空间 1.2.1 HSV颜色空间概念 1.2.2 HSV颜色模型 1.2.3 应用意义 1.3 颜色转换 1.3.1 转换方法 …

Java TCP 通信详解:从基础到实战,彻底掌握面向连接的网络编程

作为一名 Java 开发工程师&#xff0c;你一定在实际开发中遇到过需要建立稳定连接、可靠传输、有序通信等场景。这时&#xff0c;TCP&#xff08;Transmission Control Protocol&#xff09; 通信就成为你必须掌握的重要技能之一。TCP 是一种面向连接、可靠、基于字节流的传输协…

HTML5 网页游戏设计开发——1、HTML基础

前言 互联网上的应用程序被称为Web程序&#xff0c;Web引用用程序是用Web文档&#xff08;网页&#xff09;累表现用户界面&#xff0c;而Web文档都遵守HTML格式。HTML5是最新的HTML标准。之前的版本HTML4.01于1999年发布&#xff0c;小20年过去了&#xff0c;互联网已经发声了…

opencv圖片標注

功能使用python opencv, 將文字信息標注在圖片中同一張圖片中涉及多次標注文字大小為標注框的0.3倍使用多綫程運行import cv2 import threading import numpy as npdef draw_annotations(item, annotations):"""在图片上绘制标注框和文本annotations: 标注列表…

矩阵SVD分解计算

对于有数学库的时候,进行矩阵相关计算还是不复杂,但是没有数学库就很麻烦,利用算法实现了矩阵奇异值分解。 void decompose(const std::vector<std::vector<double>>& A, std::vector<std::vector<double>>& U, std::vector<dou…

Flutter基础(前端教程①⑦-Column竖直-Row水平-Warp包裹-Stack堆叠)

MainAxisAlignment 是一个枚举类&#xff0c;用于控制主轴&#xff08;Main Axis&#xff09; 方向上子组件的排列和对齐方式。MainAxisAlignment 的常用取值及效果&#xff1a;MainAxisAlignment.start子组件沿主轴的起点对齐&#xff08;Row 左对齐&#xff0c;Column 顶部对…

构建智能视频中枢--多路RTSP转RTMP推送模块在轨道交通与工业应用中的技术方案探究

1️⃣ 行业背景与技术需求&#x1f688; 轨道交通行业对视频监控的深度依赖在现代城市轨道交通系统中&#xff0c;视频监控已不仅仅是安防的一部分&#xff0c;更是贯穿于运营管理、车辆调度、应急指挥和安全保障的核心技术手段。列车车载监控 ——列车上普遍部署多路高清摄像头…

【Android Studio 2025 汉化教程】

废话不多说&#xff0c;直接上干货。 前提&#xff1a;JeBrains系列2025版已经集成中文插件&#xff0c;用户不需下载&#xff0c;只要设置下即可&#xff0c;但Android Studio并不内置也不提供汉化插件。需要工具&#xff1a; 1.IDEA&#xff08;其他JeBrains系列也可以&#…

网络安全初级(前端页面的编写分析)

源代码index.html<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>登录页面</title><!--…

RAG项目实战:LangChain 0.3集成 Milvus 2.5向量数据库,构建大模型智能应用

项目背景 最近&#xff0c;有时间&#xff0c;想着动手实战一下&#xff0c;从0到1搭建一个 RAG 系统&#xff0c;也是想通过实战的方式来更进一步学习 RAG。因此&#xff0c;就定下了以项目实战为主&#xff0c;书籍为辅的执行方式。&#xff08;书籍是黄佳老师著的《RAG 实战…

docker build 和compose 学习笔记

目录 docker build 笔记 1. 路径解析 2. 关键注意事项 2. docker compose up -d 核心区别对比 常见工作流 补充说明 1. 功能区别 2. 协作关系 场景 1&#xff1a;Compose 自动调用 Build 场景 2&#xff1a;先 Build 后 Compose 3. 关键区别 4. 为什么需要协作&…

Java学习第六十六部分——分布式系统架构

目录 一、前言提要 二、核心目标 三、核心组件与技术 1. 服务拆分与通信 2. 服务注册与发现 3. 配置中心 4. 负载均衡 5. 熔断、降级与限流 6. API 网关 7. 分布式数据管理 8. 分布式追踪与监控 9. 容器化与编排 四、典型Java分布式技术栈组合 五、关键…