Android中如何将依赖注入管控的仓库传入子视图模型
嘿,我来帮你搞定这个问题!从你描述的场景来看,你有一个Android应用的列表页面,每个AppListItemView对应自己的AppListItemViewModel,现在需要把通过依赖注入管理的仓库传递给这些子视图模型对吧?我给你分享几种实用的方案:
方案一:构造函数直接传入(简单直接)
首先,你需要修改AppListItemViewModel的构造函数,让它接收你的仓库实例。假设你的仓库类是AppRepository,调整后的ViewModel代码大概是这样:
import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import com.mikewarren.speakify.data.UserAppModel class AppListItemViewModel( private val appRepository: AppRepository, // 注入的仓库 val userAppModel: UserAppModel ) : ViewModel() { // 你的flag状态 var someFlag by mutableStateOf(false) // 现在你可以直接在ViewModel里用仓库做操作了,比如: fun updateAppStatus() { appRepository.updateApp(userAppModel.id, someFlag) } }
接下来,在父Composable或者父ViewModel中,你肯定已经通过依赖注入拿到了AppRepository实例(比如用Hilt的@Inject或者其他DI框架),然后在创建列表项ViewModel的时候直接把仓库传进去:
@Composable fun AppListScreen( // 这里通过Hilt注入仓库实例 appRepository: AppRepository = hiltViewModel() ) { // 从仓库获取用户应用列表数据 val userAppList = appRepository.getUserApps() LazyColumn { items(userAppList) { appModel -> AppListItemView( viewModel = AppListItemViewModel(appRepository, appModel) ) } } }
⚠️ 注意:这种方式直接new ViewModel的话,它不会被ViewModelStore管理,意味着配置变化(比如旋转屏幕)时ViewModel会被重建。如果你的列表项ViewModel不需要在配置变化时保留状态,这个方案完全够用;如果需要保留状态,就看下面的方案。
方案二:结合ViewModel工厂+依赖注入(支持生命周期管理)
如果你的子ViewModel需要遵循Android的ViewModel生命周期规则,那最好用ViewModelProvider的工厂来创建,结合依赖注入框架(比如Hilt)来注入仓库。
首先,给AppListItemViewModel添加@HiltViewModel注解,构造函数用@Inject标记仓库参数,同时我们可以用SavedStateHandle来传递列表项的UserAppModel相关信息(比如ID):
import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.mikewarren.speakify.data.UserAppModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class AppListItemViewModel @Inject constructor( private val appRepository: AppRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // 从SavedStateHandle获取传递过来的应用ID private val appId = savedStateHandle.get<Int>("app_id") ?: 0 var userAppModel by mutableStateOf(UserAppModel()) var someFlag by mutableStateOf(false) init { // 初始化时从仓库加载对应的应用数据 viewModelScope.launch { userAppModel = appRepository.getAppById(appId) someFlag = userAppModel.isEnabled // 假设你的模型里有这个状态 } } fun toggleFlag() { someFlag = !someFlag viewModelScope.launch { appRepository.updateAppFlag(appId, someFlag) } } }
然后,在列表项的Composable中,通过viewModel()函数获取ViewModel实例,同时传递应用ID到SavedStateHandle:
@Composable fun AppListItemView(appModel: UserAppModel) { val viewModel: AppListItemViewModel = viewModel( factory = defaultViewModelProviderFactory, key = "app_item_${appModel.id}" // 用ID作为key,确保每个列表项有独立的ViewModel ) { // 把应用ID存入SavedStateHandle savedStateHandle["app_id"] = appModel.id } // 渲染你的列表项UI,比如: Row { Text(text = appModel.name) Checkbox(checked = viewModel.someFlag, onCheckedChange = { viewModel.toggleFlag() }) } }
这种方案的好处是,每个列表项的ViewModel都会被ViewModelStore管理,配置变化时状态不会丢失,而且仓库的注入完全由Hilt管控,符合依赖注入的最佳实践。
小提醒
如果你的列表项逻辑比较简单,其实也可以不用子ViewModel,直接在Composable中调用仓库方法,用remember或者mutableStateOf来管理状态。但如果列表项有复杂的异步操作、状态逻辑,子ViewModel还是更合适的选择。
备注:内容来源于stack exchange,提问作者Mike Warren

