如何避免在多个Fragment中重复初始化ViewModel?
不需要采用单一根Fragment的实现,以下三个成熟方案都可以消除ViewModel初始化重复代码,全量适配DialogFragment、DataBinding场景,没有额外侵入性:
方案1:Kotlin委托扩展封装(零额外框架依赖,最推荐)
你现在的重复代码本质是公共构造逻辑散落在每个Fragment里,直接基于AndroidX 提供的viewModels委托API,把公共构造逻辑抽成全局扩展函数即可,和你原来手写代码的行为完全一致——每个Fragment持有独立的ViewModel实例,不会改动原有逻辑。
首先在项目公共工具类下添加扩展代码:
// 所有普通Fragment、DialogFragment都可以直接调用 inline fun <reified VM : ViewModel> Fragment.appViewModels(): Lazy<VM> { return viewModels { // 用applicationContext获取Retrofit单例,避免内存泄漏 val retrofitService = RetrofitService.getInstance(requireContext().applicationContext) val mainRepository = MainRepository(retrofitService) AppVMFactory(mainRepository) } }
后续在任意Fragment中获取ViewModel只需要一行代码,不用再重复写Retrofit初始化、Repository构造、ViewModelProvider创建的逻辑:
// 直接在Fragment成员变量位置声明即可 private val viewVM: AppViewModel by appViewModels()
- 和DataBinding配合无任何适配成本:在
onViewCreated中给binding设置lifecycleOwner = viewLifecycleOwner,直接把viewVM赋值给binding的变量即可,用法和之前完全一致。 - 如果项目还没依赖AndroidX Fragment KTX,添加官方依赖即可使用,没有兼容性问题。
方案2:Activity作用域共享ViewModel(同Activity下多Fragment复用同一实例场景用)
如果你当前Activity下的所有Fragment(包括DialogFragment)本来就需要共享同一个ViewModel的状态,直接把ViewModel作用域提升到Activity级别,整个Activity生命周期内ViewModel只会初始化一次,所有Fragment拿到的都是同一个实例。
同样用扩展封装:
inline fun <reified VM : ViewModel> Fragment.activityScopedViewModels(): Lazy<VM> { return activityViewModels { val retrofitService = RetrofitService.getInstance(requireActivity().applicationContext) val mainRepository = MainRepository(retrofitService) AppVMFactory(mainRepository) } }
使用方式和方案1一致:
private val viewVM: AppViewModel by activityScopedViewModels()
注意:该方案下ViewModel生命周期和宿主Activity绑定,Activity销毁才会触发ViewModel的onCleared,如果你的业务要求每个Fragment持有独立的ViewModel实例,不要用这个方案,选方案1即可。
方案3:依赖注入框架(中大型项目一劳永逸方案)
如果项目后续会新增更多ViewModel、Repository、网络服务依赖,直接接入Hilt依赖注入框架即可,不需要手写任何ViewModelFactory和初始化逻辑:
- 给RetrofitService、MainRepository、ViewModel类添加对应注入注解
- 直接在Fragment中用官方
by viewModels()委托即可拿到实例,Hilt会自动完成所有依赖的组装 - 天然兼容普通Fragment、DialogFragment,和DataBinding配合不需要额外适配
关于单一根Fragment方案的说明
这个方案不推荐使用:
- DialogFragment的视图挂载、生命周期管理和普通Fragment不是同一套体系,要兼容的话需要写很多特殊逻辑,侵入性极强
- 后续如果有Fragment需要挂载到其他Activity下,根Fragment的逻辑还要做额外兼容,维护成本很高
- 实现成本远高于上面三个方案,完全没有必要。
内容的提问来源于stack exchange,提问作者Andrew

