Fragment静态方法传参重建丢失致崩溃,无法复现求排查
结合Crashlytics的崩溃栈信息(尤其是BackStackRecord.executePopOps这一关键调用),这个问题的核心是Fragment被系统销毁重建后,通过newInstance传入的viewModelType没有被恢复,导致lateinit变量未初始化就被调用,最终触发UninitializedPropertyAccessException。
为什么会出现这个问题?
Android系统在内存不足、屏幕旋转、应用切后台被系统回收等场景下,会销毁处于后台的Fragment实例。当用户按下返回键触发popBackStack时,系统会重建这个Fragment,但你当前的代码是通过直接给Fragment的lateinit变量赋值来传递viewModelType——这种方式的数据不会被系统自动保存和恢复,重建后的Fragment实例是全新的,viewModelType自然处于未初始化状态,调用setActionBarTitle(viewModelType.title)时就会崩溃。
本地测试难复现,是因为日常测试很少触发系统主动销毁Fragment的场景。你可以通过**开发者选项里的「不保留活动」**来模拟:打开该选项后,切后台再返回应用,就能触发Fragment重建,大概率复现问题。
解决办法
方案1:用Fragment的Arguments传递并保存数据(推荐)
因为ColumnDetailViewModelType已经实现了Serializable,我们可以把它放到Fragment的arguments中——系统会自动保存和恢复arguments,Fragment重建时就能拿到完整的数据:
class ColumnDisplayFragment : BaseFragment() { private lateinit var viewModelType: ColumnDetailViewModelType override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 从arguments中取出数据,requireArguments()确保参数必传,避免空指针 viewModelType = requireArguments().getSerializable("VIEW_MODEL_TYPE") as ColumnDetailViewModelType } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setHasOptionsMenu(true) showBackButton(true) setActionBarTitle(viewModelType.title) recyclerView.apply { layoutManager = LinearLayoutManager(context) setHasFixedSize(true) adapter = ColumnDetailAdapter(viewModelType) } } companion object { fun newInstance(viewModelType: ColumnDetailViewModelType) = ColumnDetailFragment().apply { arguments = Bundle().apply { putSerializable("VIEW_MODEL_TYPE", viewModelType) } } } }
方案2:用ViewModel持有数据(适合复杂业务场景)
如果ColumnDetailViewModelType的实例需要跨Fragment或配置变更持久化,可以把它放到与Activity生命周期绑定的ViewModel中,确保Fragment重建时数据不会丢失:
// 定义共享ViewModel class ColumnSharedViewModel : ViewModel() { var viewModelType: ColumnDetailViewModelType? = null } // 创建Fragment时设置ViewModel数据 val sharedViewModel = ViewModelProvider(requireActivity())[ColumnSharedViewModel::class.java] sharedViewModel.viewModelType = yourViewModelInstance val fragment = ColumnDetailFragment.newInstance() // 在Fragment中获取数据 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val sharedViewModel = ViewModelProvider(requireActivity())[ColumnSharedViewModel::class.java] viewModelType = sharedViewModel.viewModelType ?: run { // 兜底处理:数据丢失时直接返回上一页 findNavController().popBackStack() return } // 后续业务逻辑... }
方案3:添加兜底检查(临时应急)
如果需要快速阻止崩溃,可以先添加lateinit变量的初始化检查,同时推进根本问题的修复:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) if (!::viewModelType.isInitialized) { Log.e("ColumnFragment", "viewModelType未初始化,执行返回操作") findNavController().popBackStack() return } // 原有业务逻辑... }
总结
最稳妥的修复方式是方案1,利用Fragment的arguments传递Serializable数据,确保Fragment在重建时能恢复必要的依赖。通过开发者选项的「不保留活动」可以快速复现问题,验证修复效果。
内容的提问来源于stack exchange,提问作者srisindhu saride

