Jetpack Compose多API调用场景下的ViewModel设计疑问
嘿,这个问题真的戳中了很多从Fragment体系转Compose开发者的痛点!其实核心思路不用太纠结,还是回到我们开发的老原则——单一职责,结合Compose的特性来灵活调整就行,我给你拆解两种常见场景:
场景1:多个API服务于同一业务域
如果这些API调用都是围绕同一个核心业务场景的(比如商品详情页,既要拉商品基础信息、又要拉用户评论、还要拉相关推荐商品),那完全可以用一个ViewModel来统一管理。
你可以在ViewModel里拆分不同的状态流(比如用StateFlow)来分别承载各个API的结果,比如:
class ProductDetailViewModel( private val productRepo: ProductRepository, private val commentRepo: CommentRepository ) : ViewModel() { // 商品信息状态 private val _productState = MutableStateFlow<Result<Product>>(Result.Loading) val productState: StateFlow<Result<Product>> = _productState // 评论列表状态 private val _commentsState = MutableStateFlow<Result<List<Comment>>>(Result.Loading) val commentsState: StateFlow<Result<List<Comment>>> = _commentsState fun loadProductData(productId: String) { viewModelScope.launch { // 并行或串行调用多个API val productDeferred = async { productRepo.getProduct(productId) } val commentsDeferred = async { commentRepo.getComments(productId) } _productState.value = productDeferred.await() _commentsState.value = commentsDeferred.await() } } }
然后在对应的Composable里,按需收集这些状态流来渲染UI就行——这样既保证了业务逻辑的集中性,又不会让ViewModel显得臃肿,各个API的职责还是清晰的。
场景2:多个API属于完全独立的业务域
如果这些API对应的是完全不相关的功能(比如一个App里既有天气查询API,又有待办事项API,两者没有任何业务关联),那千万别硬塞到一个ViewModel里,直接拆分成多个独立的ViewModel就好!
Compose里的viewModel()函数可以帮你在不同的Composable树节点获取对应的ViewModel,比如:
// 只处理天气业务的ViewModel class WeatherViewModel(private val weatherRepo: WeatherRepository) : ViewModel() { private val _weatherState = MutableStateFlow<Result<Weather>>(Result.Loading) val weatherState: StateFlow<Result<Weather>> = _weatherState fun fetchWeather(city: String) { viewModelScope.launch { _weatherState.value = weatherRepo.getWeather(city) } } } // 只处理待办业务的ViewModel class TodoViewModel(private val todoRepo: TodoRepository) : ViewModel() { private val _todosState = MutableStateFlow<Result<List<Todo>>>(Result.Loading) val todosState: StateFlow<Result<List<Todo>>> = _todosState fun fetchTodos() { viewModelScope.launch { _todosState.value = todoRepo.getTodos() } } }
然后在对应的功能Composable里分别获取:
@Composable fun WeatherScreen() { val weatherViewModel: WeatherViewModel = viewModel() // 收集天气状态、渲染UI... } @Composable fun TodoScreen() { val todoViewModel: TodoViewModel = viewModel() // 收集待办状态、渲染UI... }
这种情况下,每个ViewModel只负责自己的业务逻辑,代码更容易维护、测试,也符合单一职责原则。
额外小提示:利用ViewModel的作用域
Compose里的ViewModel默认和Activity生命周期绑定,但你也可以通过导航组件(比如Jetpack Navigation)让每个导航目的地拥有自己的ViewModel作用域——就像之前Fragment的ViewModel那样,不同页面的ViewModel相互独立,即使在同一个Activity里也不会互相干扰。
总结一下:别被“单Activity”限制住,判断ViewModel是否拆分的核心是这些API是否服务于同一个业务场景、是否需要协同工作。符合的话就用一个,不符合就拆分,Compose的ViewModel系统完全支持这种灵活的设计!
备注:内容来源于stack exchange,提问作者SmierdzoncaRobotaEhhh

