Android 12/13动态颜色致弹窗显示时Activity重启崩溃如何修复?
修复Android 12+动态颜色导致Activity重建引发的弹窗崩溃问题
针对你遇到的动态颜色触发Activity重建、弹窗持有旧Activity实例导致WindowLeaked崩溃的问题,以下是几个可行的修复方案:
方案1:让弹窗跟随Activity生命周期自动关闭
- 给弹窗绑定Activity的生命周期回调,当Activity进入
onDestroy状态时,主动关闭弹窗,彻底切断对旧Activity实例的引用。- 如果使用
DialogFragment,可直接利用其生命周期特性,在onDestroyView方法中调用dismiss()确保弹窗关闭。 - 如果是自定义弹窗或
AlertDialog,在弹窗初始化时给Activity注册ActivityLifecycleCallbacks,在onActivityDestroyed回调里执行弹窗的dismiss();同时用弱引用持有Activity,避免额外内存泄漏。
- 如果使用
- 示例代码片段:
class CustomDialog(private val activityWeakRef: WeakReference<Activity>) { private var dialog: AlertDialog? = null fun show() { val activity = activityWeakRef.get() ?: return dialog = AlertDialog.Builder(activity) .setMessage("示例弹窗") .create() dialog?.show() // 注册生命周期回调 activity.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { override fun onActivityDestroyed(activity: Activity) { dialog?.dismiss() activity.unregisterActivityLifecycleCallbacks(this) } // 实现其他空回调方法 override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} override fun onActivityStarted(activity: Activity) {} override fun onActivityResumed(activity: Activity) {} override fun onActivityPaused(activity: Activity) {} override fun onActivityStopped(activity: Activity) {} override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} }) } }
方案2:用ViewModel保存弹窗状态,重建后恢复
- 将弹窗的状态(是否显示、内容、用户输入等)存储在
ViewModel中,由于ViewModel不受Activity重建影响,能在新Activity实例中读取状态并重建弹窗。 - 步骤:
- 创建自定义ViewModel类,保存弹窗相关状态字段;
- 在Activity中初始化ViewModel,显示弹窗时更新状态;
- 当Activity重建后,在
onCreate方法中检查ViewModel的状态,若需要显示弹窗,用新的Activity实例重新创建并展示。
- 示例代码片段:
class DialogViewModel : ViewModel() { var isDialogShowing = false var dialogMessage = "" } // 在Activity中 class MainActivity : AppCompatActivity() { private val viewModel: DialogViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 重建后恢复弹窗 if (viewModel.isDialogShowing) { showDialog(viewModel.dialogMessage) } } fun showDialog(message: String) { viewModel.isDialogShowing = true viewModel.dialogMessage = message AlertDialog.Builder(this) .setMessage(message) .setOnDismissListener { viewModel.isDialogShowing = false } .show() } }
方案3:监听壁纸变化,提前关闭弹窗
- 通过
WallpaperManager注册壁纸变化监听器,在壁纸变更触发Activity重建前,主动关闭当前显示的弹窗,避免旧实例被持有。 - 示例代码片段:
class MainActivity : AppCompatActivity() { private var dialog: AlertDialog? = null private val wallpaperChangeListener = WallpaperManager.OnWallpaperChangeListener { // 壁纸变化时关闭弹窗 dialog?.dismiss() } override fun onStart() { super.onStart() getSystemService(WallpaperManager::class.java).addOnWallpaperChangeListener(wallpaperChangeListener) } override fun onStop() { super.onStop() getSystemService(WallpaperManager::class.java).removeOnWallpaperChangeListener(wallpaperChangeListener) } fun showSampleDialog() { dialog = AlertDialog.Builder(this) .setMessage("测试弹窗") .show() } }
方案4:谨慎使用Application Context创建弹窗(限部分场景)
- 对于不需要和Activity强绑定的弹窗,可尝试用Application Context创建,但需注意:
- Android 8.0+需要给弹窗设置
TYPE_APPLICATION_OVERLAY类型,同时申请SYSTEM_ALERT_WINDOW权限; - 这种弹窗不属于Activity生命周期,不会随Activity重建而关闭,需自行管理其生命周期,避免出现孤儿弹窗。
- Android 8.0+需要给弹窗设置
优先级推荐
优先选择方案1和方案2,它们更符合Android生命周期设计规范,兼容性和稳定性更强;方案3适合需要保留弹窗状态但不想用ViewModel的场景;方案4仅作为特殊场景的补充。
内容的提问来源于stack exchange,提问作者leegor
相关产品推荐
相关产品推荐

