从BottomSheetFragment切换明暗主题导致应用崩溃的解决求助
问题解决:切换主题时崩溃“You cannot start a load for a destroyed activity”
崩溃原因
直接调用AppCompatDelegate.setDefaultNightMode会触发应用全局主题重建,包括当前Activity和Fragment的重建。如果此时BottomSheetFragment依附的Activity已经处于销毁/即将销毁状态,或者重建过程中上下文失效,就会抛出这个异常。
可行实现方案
方案1:先检查Activity状态再执行切换
在切换主题前,先确认BottomSheetFragment的宿主Activity是否处于有效状态,避免在Activity销毁时执行操作:
binding.themesettingScLightdark.setOnCheckedChangeListener { _, isChecked -> val hostActivity = requireActivity() // 检查Activity是否未被销毁且未在结束流程中 if (!hostActivity.isDestroyed && !hostActivity.isFinishing) { val nightMode = if (isChecked) { AppCompatDelegate.MODE_NIGHT_YES } else { AppCompatDelegate.MODE_NIGHT_NO } AppCompatDelegate.setDefaultNightMode(nightMode) } }
方案2:通过宿主Activity处理主题切换(更可靠)
让BottomSheetFragment通过回调通知宿主Activity执行主题切换,避免Fragment直接操作全局主题导致的上下文问题:
- 在BottomSheetFragment中定义回调接口:
class ThemeBottomSheetFragment : BottomSheetDialogFragment() { private var themeChangeListener: ThemeChangeListener? = null interface ThemeChangeListener { fun onThemeChanged(isDarkMode: Boolean) } override fun onAttach(context: Context) { super.onAttach(context) // 确保宿主Activity实现了回调接口 if (context is ThemeChangeListener) { themeChangeListener = context } else { throw RuntimeException("$context must implement ThemeChangeListener") } } override fun onDetach() { super.onDetach() themeChangeListener = null // 避免内存泄漏 } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val binding = ThemesettingScBinding.inflate(inflater, container, false) binding.themesettingScLightdark.setOnCheckedChangeListener { _, isChecked -> themeChangeListener?.onThemeChanged(isChecked) dismiss() // 切换后关闭BottomSheet } // 初始化开关状态为当前主题 val currentMode = AppCompatDelegate.getDefaultNightMode() binding.themesettingScLightdark.isChecked = currentMode == AppCompatDelegate.MODE_NIGHT_YES return binding.root } }
- 在宿主Activity中实现回调接口:
class MainActivity : AppCompatActivity(), ThemeBottomSheetFragment.ThemeChangeListener { override fun onThemeChanged(isDarkMode: Boolean) { // 检查Activity状态 if (!isDestroyed && !isFinishing) { val nightMode = if (isDarkMode) { AppCompatDelegate.MODE_NIGHT_YES } else { AppCompatDelegate.MODE_NIGHT_NO } AppCompatDelegate.setDefaultNightMode(nightMode) } } }
额外注意点
- 保存开关状态:主题切换会触发Activity/Fragment重建,可通过
ViewModel或者SavedInstanceState保存开关的选中状态,重建后恢复。 - 切换后关闭BottomSheet:避免主题重建时BottomSheet重复出现,可在触发切换后调用
dismiss()关闭弹窗。
内容的提问来源于stack exchange,提问作者Suryansh Singh Rajput
相关产品推荐
相关产品推荐

