前言
有这样一种情况,这个ViewModel的构造函数有两个参数,一个参数是通过依赖注入得到的,另外一个参数是需要通过activity,fragment,或者compose 传递过来的。
@HiltViewModel
class MyHiltViewModel @Inject constructor(
private val repo: UserRepo,
private val userId: Long,
) : ViewModel() {
...
}
@Singleton
class UserRepo@Inject constructor()
方法一:@AssistedInject
这里就不贴代码了,可以移步到这里
方法二:通过SavedStateHandle
@HiltViewModel
class MyHiltViewModel @Inject constructor(
private val repo: UserRepo,
private val savedStateHandle: SavedStateHandle,
) : ViewModel() {
private val userId = savedStateHandle.get<Long>("userId")?:0L
}
1.在compose中把参数传递到viewmodel的SavedStateHandle
方法一:
如果compose所在的Activity、NavBackStackEntry 中的
intent.extras 、arguments 包含有userId的话,在ViewModel中通过savedStateHandle.get<T>()
方法可以得到对应的参数。
因为androidX会自动帮你把arguments的所有参数存到SavedStateHandle中去(前提是不用的自定义的ViewModelProvider.Factory去实例化viewModel)。
方法二:
我这里参考了hiltViewModel()
源码写了个方法去实现:
@Composable
inline fun <reified VM : ViewModel> hiltViewModelExt(
viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
"No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
},
noinline extrasProducer: (Bundle?) -> Bundle?
): VM {
val factory = createHiltViewModelFactory(viewModelStoreOwner, extrasProducer)
return viewModel(viewModelStoreOwner, factory = factory)
}
@Composable
fun createHiltViewModelFactory(
viewModelStoreOwner: ViewModelStoreOwner,
extrasProducer: (Bundle?) -> Bundle?
): ViewModelProvider.Factory? = when (viewModelStoreOwner) {
is NavBackStackEntry -> {
val navBackStackEntry = viewModelStoreOwner as NavBackStackEntry
val activity = LocalContext.current.let {
var ctx = it
while (ctx is ContextWrapper) {
if (ctx is Activity) {
return@let ctx
}
ctx = ctx.baseContext
}
throw IllegalStateException(
"Expected an activity context for creating a HiltViewModelFactory for a " +
"NavBackStackEntry but instead found: $ctx"
)
}
HiltViewModelFactory.createInternal(
activity,
navBackStackEntry,
extrasProducer(navBackStackEntry.arguments),
navBackStackEntry.defaultViewModelProviderFactory,
)
}
is ComponentActivity -> {
HiltViewModelFactory.createInternal(
viewModelStoreOwner,
viewModelStoreOwner,
extrasProducer(viewModelStoreOwner.intent?.extras),
SavedStateViewModelFactory(
viewModelStoreOwner.application,
viewModelStoreOwner,
extrasProducer(viewModelStoreOwner.intent?.extras)
)
)
}
else -> {
// Use the default factory provided by the ViewModelStoreOwner
// and assume it is an @AndroidEntryPoint annotated fragment
null
}
}
使用:
val viewModel: MyHiltViewModel = hiltViewModelExt {arg ->
(arg ?: Bundle()).apply {
putLong("userId",32465413)
//putString("name", "hiltVMTest")
}
}
2.在activity、fragment中把参数传递到viewmodel的SavedStateHandle
因为androidX会自动把arguments的所有参数存到SavedStateHandle中去,这里我就想到了一个骚操作: 直接把参数put到activity的intent或者fragment的arguments不就行了。
val viewModel:MyHiltViewModel by viewModels()
在fragment中
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments = (arguments ?: Bundle()).apply {
putString("name","Fragment HiltVMTest")
}
}
在activity中
intent.putExtra("name","Activity HiltVMTest")
打印ViewModel的这个参数:
@HiltViewModel
class MyHiltViewModel @Inject constructor(
private val repo: UserRepo,
private val savedStateHandle: SavedStateHandle,
) : ViewModel() {
private val name = savedStateHandle.get<String>("name")?:""
fun print() {
Log.d("hj", "name = $name")
}
}
ok 得到传递过来的参数了