检测和诊断崩溃问题

只要未处理的异常或信号导致意外退出,Android 应用就会崩溃。如果抛出由 Throwable 类表示的未处理异常时,使用 Java 编写的应用会崩溃。如果在执行过程中存在未处理信号(如 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 或其他问题,并找到有关异常类的文档。

类、方法、抛出异常的源文件的行号显示在堆栈轨迹的第二行。对于调用的每个函数,另一行显示前面的调用点(称为堆栈帧)。通过浏览堆栈并检查代码,您可能会发现一个传递错误值的位置。如果您的代码未出现在堆栈轨迹中,则您可能在某个地方将无效参数传递到了异常操作中。通过检查堆栈轨迹的每一行,查找您使用的任何 API 类,确认您传递的参数正确,以及您是从允许的位置调用该参数的,您通常可以明白发生了什么情况。

如需详细了解原生应用的崩溃情况,请参阅诊断原生代码崩溃

再现崩溃提示

只是启动模拟器或将设备连接到计算机,可能无法完全再现此问题。开发环境往往有更多的资源,如带宽、内存和存储。使用异常类型确定哪些资源稀少,或找到 Android 版本、设备类型或应用版本之间的关联。

内存错误

如果您遇到 OutOfMemoryError,则可以创建内存容量低的模拟器来开始操作。图 2 显示 AVD 管理器设置,可以用来控制设备上的内存量。

AVD 管理器上的内存设置

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

网络异常

由于用户经常移入或移出移动或 WLAN 网络覆盖范围,在应用网络中,异常通常不应被视为错误,而是意外发生的正常操作情况。

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

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

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

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

使用 logcat 读取

一旦您制定好再现崩溃的步骤,即可使用 logcat 等工具获取更多信息。

logcat 输出将显示您已打印的其他日志消息,以及系统中的其他消息。不要忘记关闭您已添加的任何额外 Log 语句,因为在您的应用正在运行时打印它们会浪费 CPU 和电池。