Android多模块项目如何跨模块实现Shared ViewModel数据共享
多模块架构下跨Fragment共享Activity级ViewModel的实现方案
问题根因
默认ViewModel实例不匹配的核心原因是ViewModel的存储规则:
- ViewModel存储在宿主Activity的
ViewModelStore中时,默认Key由传入的获取类的全限定名生成,格式为androidx.lifecycle.ViewModelProvider.DefaultKey:{类全路径名} - 调用
activityViewModels<CoreViewModel>()时,系统会匹配Key对应CoreViewModel的实例,找不到就通过默认工厂反射新建CoreViewModel实例存入,自然和之前存储的MainViewModel不是同一个对象。
可行实现方案(无模块耦合)
核心思路是依赖倒置+统一存储Key,子模块只依赖公共层定义的抽象契约,完全不需要感知app层的MainViewModel具体实现,不会破坏模块分层。
步骤1:在公共core模块定义共享契约
所有业务模块、app模块都依赖core模块,在这里定义跨模块共享的ViewModel抽象,不要用通用的CoreViewModel作为共享类型:
// core模块内定义,所有子模块都能引用 abstract class SharedActivityViewModel : ViewModel() { // 仅定义跨模块需要访问的字段、方法 abstract val loginState: LiveData<Boolean> abstract fun updateLoginState(isLogin: Boolean) }
如果需要更细的边界控制,也可以用interface代替抽象类。
步骤2:app层MainViewModel实现契约
// app模块内的MainViewModel,子模块完全不需要引用这个类 class MainViewModel : SharedActivityViewModel() { override val loginState = MutableLiveData<Boolean>() override fun updateLoginState(isLogin: Boolean) { loginState.value = isLogin } // 这里可以写MainViewModel私有的业务逻辑,不会暴露给子模块 }
步骤3:宿主Activity提前绑定实例
在MainActivity加载Fragment之前,主动将MainViewModel实例以公共契约类为Key存入ViewModelStore:
// app模块MainActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 初始化自身需要的MainViewModel实例 val mainVm by viewModels<MainViewModel>() // 将实例绑定到公共契约类对应的Key上 ViewModelProvider(this)[SharedActivityViewModel::class.java] = mainVm // 后续加载Fragment等正常逻辑 setContentView(R.layout.activity_main) // ... } }
步骤4:子模块Fragment直接获取实例
任意模块下挂载到MainActivity的Fragment,都可以直接通过公共契约类获取同一个共享实例:
// 任意子模块的Fragment,不需要依赖app模块 class MineFragment : Fragment() { // 直接拿到的就是MainActivity中的MainViewModel实例 private val sharedVm by activityViewModels<SharedActivityViewModel>() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 直接使用共享数据即可 sharedVm.loginState.observe(viewLifecycleOwner) { isLogin -> // 更新UI } } }
注意事项
- 不要让子模块直接依赖app模块的MainViewModel,会造成循环依赖,彻底破坏分层架构
- 公共契约中只定义跨模块需要访问的成员,不要把MainViewModel的私有业务逻辑暴露在契约中,做好边界隔离
- 不建议通过硬编码字符串Key的方式存储ViewModel,类型不安全,容易出现类型转换异常
内容的提问来源于stack exchange,提问作者ZhangAlbert
相关产品推荐
相关产品推荐

