屏幕旋转后调用DialogFragment dismiss()报错:未关联FragmentManager
解决屏幕旋转时DialogFragment调用dismiss崩溃的问题
当从父Fragment显示DialogFragment,执行耗时操作后调用dismiss()时,若期间旋转屏幕,会抛出以下异常:
java.lang.IllegalStateException: Fragment MyDialogFragment ... not associated with a fragment manager.
问题根源
屏幕旋转触发配置变更,原父Fragment和DialogFragment实例会被销毁重建,你持有的dlg引用是旧实例,它已与FragmentManager解除关联,调用dismiss()自然报错。你尝试的findFragmentByTag方法失效,是因为协程执行时父Fragment可能还未完成重建后的attach流程。
方案1:让DialogFragment自行管理自动关闭逻辑
将耗时操作和自动关闭逻辑移到DialogFragment内部,利用其自身的生命周期协程,确保只对当前存活的实例操作:
修改MyDialogFragment:
class MyDialogFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return requireActivity().let { val inflater = it.layoutInflater val v = inflater.inflate(R.layout.my_dialog, null) AlertDialog.Builder(it) .setView(v) .setTitle("title") .setNegativeButton("cancel") { dialog, _ -> dialog.cancel() } .create() } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 使用viewLifecycleOwner的协程,Dialog销毁时自动取消任务 viewLifecycleOwner.lifecycleScope.launch { delay(10000) // 模拟耗时操作 // 检查当前实例是否仍关联FragmentManager if (isAdded) { dismiss() } } } }
父Fragment的showDialog简化:
fun showDialog(){ val dlg = MyDialogFragment() dlg.show(childFragmentManager, "dlg") }
优势:逻辑内聚,无需父Fragment管理协程和Dialog引用;用户手动取消Dialog时,协程会自动终止,避免无效的dismiss()调用。
方案2:用ViewModel跨配置变更管理任务
利用ViewModel在配置变更时不会销毁的特性,让它管理耗时任务,父Fragment重建后监听任务完成事件,再查找当前存活的DialogFragment实例:
- 创建ViewModel:
class MyViewModel : ViewModel() { private val _dismissTrigger = MutableSharedFlow<Unit>() val dismissTrigger = _dismissTrigger.asSharedFlow() fun startDelayedDismiss() { viewModelScope.launch { delay(10000) // 模拟耗时操作 _dismissTrigger.emit(Unit) } } }
- 修改父Fragment:
class MyFragment: Fragment() { private val viewModel by viewModels<MyViewModel>() private val dialogTag = "dlg" override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 监听任务完成事件,仅在Fragment处于活跃状态时处理 viewLifecycleOwner.lifecycleScope.launch { viewModel.dismissTrigger.collect { val currentDialog = childFragmentManager.findFragmentByTag(dialogTag) as? DialogFragment currentDialog?.dismiss() } } } fun showDialog(){ val dlg = MyDialogFragment() dlg.show(childFragmentManager, dialogTag) viewModel.startDelayedDismiss() } }
优势:适合耗时任务需要和父Fragment共享状态的场景,ViewModel确保任务不会因配置变更中断。
内容的提问来源于stack exchange,提问作者Mehdi I.
相关产品推荐
相关产品推荐

