返回

从源码的角度揭秘:为何 Dialog 无法在 Application 中展示

Android

技术博客文章范例

引言

Dialog 是一种重要的 Android 组件,用于在应用中展示各种信息和交互元素。然而,当开发者尝试在 Application 类中展示 Dialog 时,往往会遭遇错误:

android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?

本文将带领大家深入源码,逐一剖析这一错误的成因,并揭示 Token 的真面目,为开发者提供深刻的理解和应对之策。

错误追踪

为了更好地理解问题,我们先创建一个简单的 Demo:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val dialog = AlertDialog.Builder(application)
        dialog.setMessage("Hello World!")
        dialog.show()
    }
}

运行此 Demo,你将看到上述错误:

android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?

Token 的秘密

上述错误提示中的 Token 是一个至关重要的概念。在 Android 系统中,Token 代表着一个正在运行的 Activity 的唯一标识符。当 Dialog 尝试在 Application 类中展示时,由于 Application 本身不具备 Activity 的属性,因此无法为 Dialog 提供有效的 Token。

源码探究

让我们深入源码来一探究竟。在 ActivityThread 类的 addWindow 方法中,负责添加 Dialog 窗口到 WindowManager 的代码如下:

if (token == null) {
    throw new BadTokenException(
            "Unable to add window -- token null is not valid; is your activity running?");
}

由此可见,如果 Token 为 null,则会抛出 BadTokenException 异常。

Activity 的生命周期

Activity 的生命周期是一个关键因素。当 Activity 被销毁后,其 Token 也会随之失效。因此,在 Application 类中展示 Dialog 时,需要确保当前 Activity 仍处于运行状态。

解决之道

解决上述错误的方法很简单,就是在展示 Dialog 之前检查当前 Activity 的状态。如果 Activity 已被销毁,则需要重新创建它。

val activity = application.currentActivity
if (activity != null && activity.isFinishing) {
    activity.recreate()
}

val dialog = AlertDialog.Builder(activity)
dialog.setMessage("Hello World!")
dialog.show()

总结

通过深入源码的分析,我们了解到 Dialog 无法在 Application 中展示的原因在于 Token 的缺失。Token 代表着正在运行的 Activity 的唯一标识符,在 Application 类中,由于缺乏 Activity 的属性,无法提供有效的 Token。因此,开发者在展示 Dialog 之前,需要确保当前 Activity 处于运行状态,或重新创建 Activity 以获取新的 Token。