Fragment中ViewModel屏幕旋转丢失数据,求排查原因
看起来你遇到的核心问题是ViewModel在屏幕旋转时被意外重新创建了,导致uiModel被重置为null——这和ViewModel本该在配置变化时保留数据的特性不符,咱们一步步拆解原因和解决办法:
最可能的原因
你代码里的ViewModel绑定的是Fragment的生命周期,但如果你的Activity在onCreate方法中每次都创建新的DiaryFragment实例(比如直接new DiaryFragment()并替换容器),那么当屏幕旋转触发Activity重建时,新的Fragment实例会拥有全新的ViewModelStore,导致ViewModel被重新初始化,uiModel自然就变回null了。
另外,你使用的ViewModelProviders.of(this)是较旧的API,虽然不是直接原因,但也可能带来一些潜在问题,推荐替换为最新的ViewModelProvider。
修复步骤
1. 修正Activity中Fragment的创建逻辑
在Activity的onCreate里,只有当savedInstanceState为null时(也就是首次启动Activity)才创建新的Fragment,否则让FragmentManager自动恢复之前的Fragment实例:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 仅在首次启动时创建Fragment if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .replace(R.id.container, DiaryFragment()) .commit() } }
这样屏幕旋转时,Fragment会被系统自动恢复,它绑定的ViewModel也会被保留,uiModel就不会被重置了。
2. 替换为最新的ViewModelProvider API
把Fragment中初始化ViewModel的代码替换为:
// 替换旧的 ViewModelProviders.of(this).get(...) viewModel = ViewModelProvider(this).get(DiaryViewModel::class.java)
这个新API更简洁,也避免了旧API的一些潜在问题。
3. 优化ViewModel的初始化逻辑
用lazy委托来初始化uiModel,可以避免手动判断null的麻烦,确保它只会被初始化一次:
class DiaryViewModel: ViewModel() { private val uiModel: MutableLiveData<DiaryUIModel> by lazy { MutableLiveData<DiaryUIModel>().apply { value = DiaryUIModel() } } fun getModel(): LiveData<DiaryUIModel> = uiModel }
为什么这样能解决问题?
当屏幕旋转时,Activity会重建,但savedInstanceState不为null,所以不会创建新的Fragment。FragmentManager会恢复之前的Fragment实例,而该Fragment的ViewModelStore会被保留,ViewModel也就不会被重新创建,uiModel里的数据自然就保留下来了。
内容的提问来源于stack exchange,提问作者Algar

