Android ViewModel构造函数调用时机及含SavedStateHandle的多参数如何传入工厂?
问题解答
1. ViewModel构造函数的调用时机
- 首次调用
ViewModelProvider.get(SavedStateViewModel.class)获取对应ViewModel实例时,构造函数会被触发执行 - 同一个
ViewModelStoreOwner(如对应的Activity/Fragment)的生命周期内,重复调用get方法不会重复执行构造函数,会直接返回已缓存的实例 - 仅当ViewModelStoreOwner彻底销毁(如Activity正常finish、Fragment被正式移除)后,下次重新创建该Owner时调用get方法,才会再次触发构造函数执行,配置变更(如屏幕旋转)不会触发构造函数重新执行。
2. SavedStateHandle参数的获取逻辑
你示例中不需要手动传参就能拿到SavedStateHandle实例,是因为ViewModelProvider默认使用SavedStateViewModelFactory来实例化ViewModel,该工厂会自动完成以下流程:
- 读取当前Activity的Intent extras或Fragment的arguments作为初始数据
- 封装生成对应的
SavedStateHandle实例 - 匹配ViewModel构造函数的参数类型,检测到需要
SavedStateHandle参数时自动注入该实例,不需要开发者手动传入。
3. 混合Hilt依赖、运行时参数、SavedStateHandle的实现示例
如果你的ViewModel需要同时传入Hilt注入的依赖、运行时动态参数、SavedStateHandle,推荐使用Hilt的辅助注入功能实现,示例如下:
第一步:定义ViewModel,声明辅助注入
import androidx.lifecycle.ViewModel import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.lifecycle.HiltViewModel import dagger.assisted.Assisted import dagger.assisted.AssistedInject // 声明辅助工厂交给Hilt生成实现 @HiltViewModel(assistedFactory = MyViewModel.Factory::class) class MyViewModel @AssistedInject constructor( // 由Hilt自动注入的依赖,无需加@Assisted注解 private val userRepository: UserRepository, private val postRepository: PostRepository, // 运行时传入的动态参数,加@Assisted注解 @Assisted private val categoryId: Int, // 用于进程恢复的SavedStateHandle,加@Assisted注解 @Assisted private val savedStateHandle: SavedStateHandle ) : ViewModel() { // 定义辅助工厂接口 interface Factory { fun create(categoryId: Int, savedStateHandle: SavedStateHandle): MyViewModel } // 此处编写ViewModel的业务逻辑 }
第二步:在Activity/Fragment中实例化ViewModel
import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class DemoActivity : AppCompatActivity() { // 从Intent中获取运行时参数:分类ID private val categoryId by lazy { intent.getIntExtra("KEY_CATEGORY_ID", 0) } // 实例化ViewModel private val viewModel: MyViewModel by viewModels { SavedStateViewModelFactory(application, this, intent.extras).create( key = null, modelClass = MyViewModel::class.java ) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 直接使用viewModel即可 } }
如果不想用Hilt辅助注入,也可以自定义继承AbstractSavedStateViewModelFactory的工厂类实现,示例如下:
import androidx.lifecycle.AbstractSavedStateViewModelFactory import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.savedstate.SavedStateRegistryOwner class CustomViewModelFactory( private val userRepository: UserRepository, private val categoryId: Int, owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) { override fun <T : ViewModel> create(key: String, modelClass: Class<T>, handle: SavedStateHandle): T { @Suppress("UNCHECKED_CAST") return MyViewModel(userRepository, categoryId, handle) as T } }
在Activity中使用自定义工厂:
@AndroidEntryPoint class DemoActivity : AppCompatActivity() { // 注入Hilt管理的Repository实例 @Inject lateinit var userRepository: UserRepository private val categoryId by lazy { intent.getIntExtra("KEY_CATEGORY_ID", 0) } private val viewModel: MyViewModel by viewModels { CustomViewModelFactory(userRepository, categoryId, this, intent.extras) } }
内容的提问来源于stack exchange,提问作者Neeraja Gandla
相关产品推荐
相关产品推荐

