2023年Android Hilt注入辅助参数至ViewModel的更优方案问询
问题
在2023年,除了已知的那种方式外,有没有更优或更少样板代码的方案,能将Assisted参数注入到ViewModel的构造方法中?
当前ViewModel实现代码:
class PlantDetailViewModel @AssistedInject constructor( plantRepository: PlantRepository, @Assisted private val savedStateHandle: SavedStateHandle, @Assisted private val plantId: String ) : ViewModel() { @AssistedFactory interface PlantDetailViewModelFactory { fun create(handle: SavedStateHandle, plantId: String): PlantDetailViewModel } companion object { fun provideFactory( assistedFactory: PlantDetailViewModelFactory, owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null, plantId: String ): AbstractSavedStateViewModelFactory = object : AbstractSavedStateViewModelFactory(owner, defaultArgs) { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, handle: SavedStateHandle): T { return assistedFactory.create(handle, plantId) as T } } } }
Fragment中的调用代码:
@AndroidEntryPoint class PlantDetailFragment : Fragment() { private val args: PlantDetailFragmentArgs by navArgs() @Inject lateinit var plantDetailViewModelFactory: PlantDetailViewModelFactory private val plantDetailViewModel: PlantDetailViewModel by viewModels { PlantDetailViewModel.provideFactory(plantDetailViewModelFactory, this, arguments, args.plantId) } }
更简洁的实现方案
方案1:利用Hilt的@HiltViewModel配合SavedStateHandle传递参数
如果项目已经接入Hilt,可以直接借助SavedStateHandle传递Assisted参数,完全省去手动创建工厂类的样板代码:
- 修改ViewModel:
@HiltViewModel class PlantDetailViewModel @Inject constructor( plantRepository: PlantRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // 直接从SavedStateHandle中获取plantId private val plantId: String = savedStateHandle.get<String>("plantId")!! }
- Fragment中调用:
@AndroidEntryPoint class PlantDetailFragment : Fragment() { private val args: PlantDetailFragmentArgs by navArgs() // 通过默认创建扩展传递参数到SavedStateHandle private val plantDetailViewModel: PlantDetailViewModel by hiltViewModel( extras = defaultViewModelCreationExtras.apply { setSavedStateHandleArgs(mapOf("plantId" to args.plantId)) } ) }
方案2:用Kotlin扩展函数简化工厂创建逻辑
如果必须保留@AssistedInject的方式,可以把通用的工厂创建逻辑抽成扩展函数,减少ViewModel内的重复代码:
- 定义通用扩展函数:
inline fun <reified T : ViewModel> createAssistedSavedStateFactory( owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null, crossinline create: (SavedStateHandle) -> T ): AbstractSavedStateViewModelFactory { return object : AbstractSavedStateViewModelFactory(owner, defaultArgs) { @Suppress("UNCHECKED_CAST") override fun <VM : ViewModel?> create(key: String, modelClass: Class<VM>, handle: SavedStateHandle): VM { return create(handle) as VM } } }
- 修改ViewModel(移除companion内的provideFactory方法):
class PlantDetailViewModel @AssistedInject constructor( plantRepository: PlantRepository, @Assisted private val savedStateHandle: SavedStateHandle, @Assisted private val plantId: String ) : ViewModel() { @AssistedFactory interface PlantDetailViewModelFactory { fun create(handle: SavedStateHandle, plantId: String): PlantDetailViewModel } }
- Fragment中调用:
@AndroidEntryPoint class PlantDetailFragment : Fragment() { private val args: PlantDetailFragmentArgs by navArgs() @Inject lateinit var plantDetailViewModelFactory: PlantDetailViewModelFactory private val plantDetailViewModel: PlantDetailViewModel by viewModels { createAssistedSavedStateFactory(this, arguments) { handle -> plantDetailViewModelFactory.create(handle, args.plantId) } } }
方案3:借助AssistedInject的Kotlin委托库简化调用
引入AssistedInject的Kotlin扩展库,可以进一步简化Fragment中的ViewModel初始化:
先添加依赖:
implementation "com.squareup.inject:assisted-inject-ktx:0.7.0"
然后Fragment中直接使用委托:
@AndroidEntryPoint class PlantDetailFragment : Fragment() { private val args: PlantDetailFragmentArgs by navArgs() @Inject lateinit var factory: PlantDetailViewModelFactory private val viewModel: PlantDetailViewModel by assistedViewModels { factory.create(savedStateHandle, args.plantId) } }
内容的提问来源于stack exchange,提问作者Nexussim Lements
相关产品推荐
相关产品推荐

