在ViewModel架构的KMM应用中使用collectAsState及代码优化咨询
问题分析与优化方案
一、collectAsState的正确使用
UI层
你的写法可以简化,collectAsState返回的是State<T>对象,直接获取其value即可使用,无需用apply包裹:
val userState = usersViewModel.userInfo.collectAsState(initial = null) // 使用 userState.value 访问用户数据
如果是在Composable函数中,这种写法会自动跟随Compose生命周期收集流,避免内存泄漏。
ViewModel层
建议将userInfo转换为StateFlow,确保配置变更时数据不丢失,同时更符合ViewModel的状态管理规范:
val userInfo = repo.getUserProfile() .catch { println("获取用户信息失败: $it") } .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), // 5秒无订阅则停止收集,节省资源 initialValue = null )
通过stateIn将冷流转为热流,WhileSubscribed策略能避免后台不必要的流收集。
Repository层
getUserProfile的写法冗余,Firestore的snapshots本身就是Flow,无需手动嵌套flow和collect,直接转换即可:
fun getUserProfile(): Flow<User?> = firestore.collection("User") .document(auth.currentUser?.uid ?: return flowOf(null)) // 避免!!空指针风险 .snapshots .map { it.data<UserInfo>() } .catch { println("获取用户快照失败: $it") emit(null) // 发射null避免流终止 }
同时用安全调用处理currentUser,消除空指针隐患。
二、getAllRestaurants的写法问题
当前实现存在两个核心问题:
val user = getUserProfile()拿到的是Flow,在map中调用user.first()!!会每次餐厅快照更新时都重新挂起获取用户数据,重复请求且阻塞流处理,这是性能慢的主要原因之一。!!会引发空指针崩溃,如果用户数据未加载完成或获取失败,直接导致程序异常。
正确的写法是使用combine合并用户流和餐厅流,在流的组合中完成过滤:
fun getAllRestaurants(): Flow<List<Restaurant>> = combine( getUserProfile(), firestore.collection("Restaurant").snapshots ) { user, querySnapshot -> val userLocation = user?.userLocation ?: return@combine emptyList() querySnapshot.documents.mapNotNull { it.data<Restaurant>() } // 过滤解析失败的餐厅 .filter { restaurant -> checkIfPointInsidePolygon(userLocation, restaurant.polygonList) } }.catch { println("获取餐厅列表失败: $it") emit(emptyList()) }
combine会在任意一个流更新时触发计算,确保用户数据和餐厅数据同步。mapNotNull过滤无效的餐厅数据,避免后续处理null。- 安全获取
userLocation,用户数据未加载时返回空列表而非崩溃。
三、性能慢的排查方向
- 调试环境影响:调试模式下KMM会有额外的日志、断点检查,性能会比Release模式差,建议打包Release版本测试真实性能。
- Firestore数据量:如果餐厅数据量过大,每次快照更新都要遍历所有文档并执行
checkIfPointInsidePolygon计算,会导致卡顿。可以考虑:- 在Firestore端添加地理位置过滤,减少客户端需要处理的数据量。
- 缓存多边形判断的计算结果,避免重复执行。
- 流收集效率:确保ViewModel中使用
stateIn的SharingStarted.WhileSubscribed策略,避免后台不必要的流收集,减少资源消耗。
内容的提问来源于stack exchange,提问作者Cipri
相关产品推荐
相关产品推荐

