最近新版本上线以后,firebase上出现了比较多的崩溃,崩溃日志如下:
页面结构是Activity中添加Fragment,Activity和Fragment都使用了ViewBinding。复现步骤是在切换系统语言以后,再回到界面调用一个方法,方法中使用了ViewBinding对象。
因为切换系统语言以后,Fragment走了onDestroy和onDestroyView方法,Fragment被销毁了。Activity中是持有对应的Fragment实例的,这会出现问题。
setRetainInstance
Fragment提供了setRetainInstance方法,在设置为true的时候,是不会被销毁的,因此开始想到的就是这种方式。但是这种方式会出现问题,就是Fragment的生命周期会出现变化。而且官方也弃用setRetainInstance了,具体原因是:
- 内存泄漏:保留的 Fragments 保存了对旧 Activity 的引用,如果处理不当可能会导致内存泄漏
- 困惑的状态管理:复杂的状态交互可能会导致意外行为和视图生命周期问题
- 紧密耦合:使用保留片段可能会导致视图层与数据和操作紧密耦合,从而使代码更难维护
官方建议:ViewModel是配置更改时保留状态的主要解决方案。ViewModel的设计具有生命周期感知功能,使其成为保存UI状态数据和在 Activity 重新创建后继续使用的较优方式
经过综合考虑,最终放弃了这种方式解决问题。
Fragment添加
之前的Fragment添加方式如下:
val fragmentTag = SimpleContentFragment::class.simpleName
val findFragment = supportFragmentManager.findFragmentByTag(fragmentTag)
if (!contentFragment.isAdded && findFragment == null) {
supportFragmentManager.beginTransaction().add(R.id.contentContainer, contentFragment, fragmentTag).commitAllowingStateLoss()
} else {
supportFragmentManager.beginTransaction().show(contentFragment).commitAllowingStateLoss()
}
因为修改完系统语言回到APP以后,Fragment还在,因此走了show方法。于是对此处代码进行改进,让其重走生命周期:
val fragmentTag = SimpleContentFragment::class.simpleName
val findFragment = supportFragmentManager.findFragmentByTag(fragmentTag)
if (findFragment != null) {
supportFragmentManager.beginTransaction().remove(findFragment)}
supportFragmentManager.beginTransaction().add(R.id.contentContainer, contentFragment, fragmentTag).commitAllowingStateLoss()
最终解决了上面的问题。
根本原因:系统语言更改导致Activity重建,Fragment被销毁了
感谢大家的支持,如有错误请指正,如需转载请标明原文出处!