第三章MVVM 架构与 ViewModelMVVM Model View ViewModel核心目标是UI 与业务逻辑分离。3.1 MVC vs MVVM 对比架构角色数据流向MVCModel View ControllerView → Controller → Model → ViewMVVMModel View ViewModelView ← DataBinding ← ViewModel ← Model传统 MVC 问题Activity/Fragment 既要处理 UI又要处理业务逻辑代码臃肿难以测试MVVM 优势ViewModel 专注管理 UI 状态ViewComposable只负责渲染业务逻辑与 UI 解耦3.2 ViewModel 的生命周期classHomeViewModel:ViewModel(){privateval_uiStateMutableStateFlowHomeUiState(HomeUiState.Loading)valuiState:StateFlowHomeUiState_uiState.asStateFlow()init{loadData()// ViewModel 创建时自动加载数据}funloadData(){viewModelScope.launch{_uiState.valueHomeUiState.Loadingtry{valresultArticleRepository.getArticleList()sourceListresult.toMutableList()_uiState.valueHomeUiState.Success(articlesresult)}catch(e:Exception){_uiState.valueHomeUiState.Error(e.message?:加载失败)}}}}ViewModel 创建方式ComposablefunHomePage(navController:NavController){valviewModel:HomeViewModelviewModel()// 自动关联生命周期valuiStatebyviewModel.uiState.collectAsState()// ...}viewModel()是 lifecycle-viewmodel-compose 提供的委托函数会自动管理 ViewModel 的创建与销毁。3.3 UiState 密封接口三个页面统一使用 sealed interface 定义 UI 状态sealedinterfaceHomeUiState{dataobjectLoading:HomeUiStatedataclassSuccess(valarticles:ListArticleBean,valsearchKeyword:String):HomeUiStatedataclassError(valmessage:String):HomeUiState}优势类型安全when 表达式 exhaustive明确区分 Loading/Success/Error 三种状态便于 Compose 逐个处理3.4 数据流向UDF 模式单向数据流Unidirectional Data Flow用户操作 → ViewModel 更新 State → Compose 重组 UI ↑ ↓ └────────────── 回调 ◀───────────────┘首页搜索示例// ViewModelfunupdateSearchKeyword(keyword:String){valfilteredif(keyword.isBlank()){sourceList}else{sourceList.filter{it.title.contains(keyword,ignoreCasetrue)}}_uiState.valueHomeUiState.Success(articlesfiltered,searchKeywordkeyword)}// ComposableOutlinedTextField(valueuiState.searchKeyword,onValueChangeviewModel::updateSearchKeyword,// 回调传回 ViewModellabel{Text(搜索内容)},)3.5 三个页面的 ViewModel 对比页面ViewModel数据来源首页HomeViewModelArticleRepository网络获取个人中心ProfileViewModelProfileRepository本地示例详情页DetailViewModel(articleId)ArticleRepository内存缓存详情页 ViewModel 工厂模式带参数classDetailViewModel(privatevalarticleId:Int):ViewModel(){// ...}classDetailViewModelFactory(privatevalarticleId:Int):ViewModelProvider.Factory{overridefunT:ViewModelcreate(modelClass:ClassT):T{returnDetailViewModel(articleId)asT}}// Composable 创建方式valviewModel:DetailViewModelviewModel(keydetail_$articleId,factoryDetailViewModelFactory(articleId),)3.6 ViewModel 注意事项注意点说明禁止持有 ContextViewModel 不应引用 Activity/View使用 viewModelScope协程作用域绑定 ViewModel 生命周期避免 GlobalScope会导致协程泄漏状态不可变暴露对外暴露 StateFlow内部用 MutableStateFlow// ✅ 正确不可变暴露privateval_uiStateMutableStateFlowHomeUiState(HomeUiState.Loading)valuiState:StateFlowHomeUiState_uiState.asStateFlow()// ✅ 正确在 viewModelScope 中执行viewModelScope.launch{// 网络请求}// ❌ 错误禁止持有 ContextclassBadViewModel(privatevalcontext:Context):ViewModel()// 不要这样做3.7 总结ViewModel 管理 UI 状态业务逻辑与 UI 分离sealed interface 定义 UiState类型安全单向数据流用户操作 → ViewModel → UI 重组viewModel() 自动管理生命周期避免内存泄漏详情页需要带参数 ViewModel 时使用 Factory 模式上一章第二章Compose 入门 — 声明式 UI 编程 下一章第四章Navigation Compose 页面导航
第三章:MVVM 架构与 ViewModel
第三章MVVM 架构与 ViewModelMVVM Model View ViewModel核心目标是UI 与业务逻辑分离。3.1 MVC vs MVVM 对比架构角色数据流向MVCModel View ControllerView → Controller → Model → ViewMVVMModel View ViewModelView ← DataBinding ← ViewModel ← Model传统 MVC 问题Activity/Fragment 既要处理 UI又要处理业务逻辑代码臃肿难以测试MVVM 优势ViewModel 专注管理 UI 状态ViewComposable只负责渲染业务逻辑与 UI 解耦3.2 ViewModel 的生命周期classHomeViewModel:ViewModel(){privateval_uiStateMutableStateFlowHomeUiState(HomeUiState.Loading)valuiState:StateFlowHomeUiState_uiState.asStateFlow()init{loadData()// ViewModel 创建时自动加载数据}funloadData(){viewModelScope.launch{_uiState.valueHomeUiState.Loadingtry{valresultArticleRepository.getArticleList()sourceListresult.toMutableList()_uiState.valueHomeUiState.Success(articlesresult)}catch(e:Exception){_uiState.valueHomeUiState.Error(e.message?:加载失败)}}}}ViewModel 创建方式ComposablefunHomePage(navController:NavController){valviewModel:HomeViewModelviewModel()// 自动关联生命周期valuiStatebyviewModel.uiState.collectAsState()// ...}viewModel()是 lifecycle-viewmodel-compose 提供的委托函数会自动管理 ViewModel 的创建与销毁。3.3 UiState 密封接口三个页面统一使用 sealed interface 定义 UI 状态sealedinterfaceHomeUiState{dataobjectLoading:HomeUiStatedataclassSuccess(valarticles:ListArticleBean,valsearchKeyword:String):HomeUiStatedataclassError(valmessage:String):HomeUiState}优势类型安全when 表达式 exhaustive明确区分 Loading/Success/Error 三种状态便于 Compose 逐个处理3.4 数据流向UDF 模式单向数据流Unidirectional Data Flow用户操作 → ViewModel 更新 State → Compose 重组 UI ↑ ↓ └────────────── 回调 ◀───────────────┘首页搜索示例// ViewModelfunupdateSearchKeyword(keyword:String){valfilteredif(keyword.isBlank()){sourceList}else{sourceList.filter{it.title.contains(keyword,ignoreCasetrue)}}_uiState.valueHomeUiState.Success(articlesfiltered,searchKeywordkeyword)}// ComposableOutlinedTextField(valueuiState.searchKeyword,onValueChangeviewModel::updateSearchKeyword,// 回调传回 ViewModellabel{Text(搜索内容)},)3.5 三个页面的 ViewModel 对比页面ViewModel数据来源首页HomeViewModelArticleRepository网络获取个人中心ProfileViewModelProfileRepository本地示例详情页DetailViewModel(articleId)ArticleRepository内存缓存详情页 ViewModel 工厂模式带参数classDetailViewModel(privatevalarticleId:Int):ViewModel(){// ...}classDetailViewModelFactory(privatevalarticleId:Int):ViewModelProvider.Factory{overridefunT:ViewModelcreate(modelClass:ClassT):T{returnDetailViewModel(articleId)asT}}// Composable 创建方式valviewModel:DetailViewModelviewModel(keydetail_$articleId,factoryDetailViewModelFactory(articleId),)3.6 ViewModel 注意事项注意点说明禁止持有 ContextViewModel 不应引用 Activity/View使用 viewModelScope协程作用域绑定 ViewModel 生命周期避免 GlobalScope会导致协程泄漏状态不可变暴露对外暴露 StateFlow内部用 MutableStateFlow// ✅ 正确不可变暴露privateval_uiStateMutableStateFlowHomeUiState(HomeUiState.Loading)valuiState:StateFlowHomeUiState_uiState.asStateFlow()// ✅ 正确在 viewModelScope 中执行viewModelScope.launch{// 网络请求}// ❌ 错误禁止持有 ContextclassBadViewModel(privatevalcontext:Context):ViewModel()// 不要这样做3.7 总结ViewModel 管理 UI 状态业务逻辑与 UI 分离sealed interface 定义 UiState类型安全单向数据流用户操作 → ViewModel → UI 重组viewModel() 自动管理生命周期避免内存泄漏详情页需要带参数 ViewModel 时使用 Factory 模式上一章第二章Compose 入门 — 声明式 UI 编程 下一章第四章Navigation Compose 页面导航