崩溃

未处理的异常或信号导致的意外退出,会使 Android 应用崩溃。使用 Java 编写的应用会在抛出未处理的异常(由 Throwable 类表示)时崩溃。使用原生代码语言编写的应用,会在执行过程中遇到未处理的信号(如 SIGSEGV)时崩溃。

当应用崩溃时,Android 会终止应用的进程并显示一个对话框,告知用户应用已停止,如图 1 所示。

Android 设备上的应用崩溃

图 1 Android 设备上崩溃的应用

应用并不是只有在前台运行时才会崩溃。任何应用组件,甚至是广播接收器或内容提供程序这类在后台运行的组件,都有可能导致应用崩溃。这些崩溃常常让用户感到困惑,因为他们并没有在使用应用。

如果您的应用发生了崩溃,您可以使用本页中的指南来诊断和解决问题。有关如何诊断使用原生代码语言编写的应用中的崩溃的指南,请参阅诊断原生代码崩溃

检测问题

您可能并不总是能够知道用户在使用您的应用时遇到了过多的崩溃。如果您已发布了您的应用,Android Vitals 可以帮助您认识到这个问题。

Android Vitals

当您的应用崩溃次数太多时,Android Vitals 可通过 Play 管理中心提醒您,从而帮助您改进应用性能。当应用出现以下情况时,Android Vitals 会认为崩溃次数超出了正常范围:

  • 每天至少有 1.09% 的工作时段出现了至少一次崩溃。
  • 每天至少有 0.18% 的工作时段出现了两次或两次以上的崩溃。

每日工作时段是指应用在一天内被使用的时间。要了解 Google Play 如何收集 Android Vitals 数据,请参阅 Play 管理中心文档。

在了解到应用崩溃次数太多之后,下一步就是诊断崩溃。

诊断崩溃

解决崩溃问题可能很困难。但是,如果您能确定崩溃的根本原因,就有很可能能够找到解决方法。

有很多情况会导致应用崩溃。有些原因很明显,例如检查 null 值或空字符串,而其他一些原因则不太容易察觉,例如向 API 传递了无效的参数,或者是复杂的多线程互动。

读取堆栈轨迹

解决崩溃的第一步是确定它发生的位置。如果您使用的是 Play 管理中心或 logcat 工具的输出,则可使用报告详细信息中提供的堆栈轨迹。如果您没有堆栈轨迹可用,则应通过手动测试应用或联系受影响的用户在本地再现崩溃,并且要在使用 logcat 的情况下再现。

以下轨迹显示了示例应用上的一个崩溃示例:

--------- beginning of crash
    AndroidRuntime: FATAL EXCEPTION: main
    Process: com.android.developer.crashsample, PID: 3686
    java.lang.NullPointerException: crash sample
    at com.android.developer.crashsample.MainActivity$1.onClick(MainActivity.java:27)
    at android.view.View.performClick(View.java:6134)
    at android.view.View$PerformClick.run(View.java:23965)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:156)
    at android.app.ActivityThread.main(ActivityThread.java:6440)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:746)
    --------- beginning of system
    

堆栈轨迹显示对调试崩溃至关重要的两条信息:

  • 抛出的异常的类型。
  • 抛出异常的代码段。

抛出的异常的类型通常是关于问题原因的非常重要的提示。查看是否为 IOExceptionOutOfMemoryError 或其他问题,并找到有关 exception 类的文档。

抛出异常的源文件的行号显示在堆栈轨迹的第二行。对于调用的每个函数,会有另一行显示前面的调用点(称为堆栈帧)。通过遍历堆栈并检查代码,您可能会发现传递错误值的位置。如果您的代码没有出现在堆栈轨迹中,则表示您可能在某个地方将无效参数传递给了异步操作。您通常可以通过以下方式确定发生了什么问题:检查堆栈轨迹的每一行,查找所使用的任何 API 类,确认传递的参数是否正确,以及是否从允许的位置调用了参数。

要详细了解原生应用中的崩溃问题,请参阅诊断原生代码崩溃

有关再现崩溃的建议

再现问题可能无法简单地通过启动模拟器或将设备连接到计算机来实现。开发环境往往具有更多的资源,比如带宽、内存和存储。通过异常类型确定哪项资源不足,或找到 Android 版本、设备类型或应用版本之间的关联。

内存错误

如果发生了 OutOfMemoryError 异常,您可以首先创建一个内存容量较低的模拟器。图 2 显示了 AVD 管理器设置,您可以通过这些设置控制设备的内存容量。

AVD 管理器中的内存设置

图 2. AVD 管理器上的内存设置

网络异常

由于用户经常进出移动或 Wi-Fi 网络覆盖范围,在应用中,网络异常通常不应被视为错误,而应被视为偶尔会出现的正常运行环境。

如果您需要再现网络异常(比如 UnknownHostException),请在您的应用尝试使用网络时打开飞行模式。

此外,也可以通过选择网速模拟和/或网络延迟,降低模拟器中的网络质量。您可以使用 AVD 管理器上的 SpeedLatency 设置,也可以启动带有 -netdelay-netspeed 标记的模拟器,如以下命令行示例中所示:

emulator -avd [your-avd-image] -netdelay 20000 -netspeed gsm
    

本例为所有网络请求设置了 20 秒的延迟,并且将上传和下载速度设置为 14.4 Kbps。要详细了解模拟器的命令行选项,请参阅从命令行启动模拟器

使用 logcat 读取

在您找到再现崩溃的方法后,可以使用 logcat 等工具获取更多信息。

logcat 输出将显示您输出的其他日志消息,以及系统输出的日志消息。请记住关闭您添加的任何额外的 Log 语句,因为在应用运行时输出这些日志会浪费 CPU 和电池电量。