
图 1. 显示给用户的 ANR 对话框。
本文档介绍了 Android 系统如何判断应用是否无响应,以及如何确保您的应用响应灵敏。
无论您的代码写得多么好,您的应用仍然可能出现卡顿、挂起、长时间冻结或处理输入时间过长的情况。如果您的应用在前台无响应,用户会看到“应用无响应”(ANR) 对话框(如图 1 所示)。ANR 对话框允许用户强制退出应用。如果应用不在前台,则会悄无声息地停止。在应用设计中加入响应能力至关重要,以最大限度地减少 ANR 对话框的出现。
ANR 触发器
通常,如果应用无法在主线程(也称为 UI 线程)上响应用户输入,从而导致系统无法处理传入的用户输入事件,则系统会显示 ANR。
例如,如果应用在 UI 线程上执行阻塞 I/O 操作(例如网络访问),则可能发生 ANR。另一个示例是,当应用在 UI 线程上花费过多时间构建复杂的内存中结构或计算游戏中的下一步时,也可能发生 ANR。
在 Android 中,应用响应能力由 ActivityManager
和 WindowManager
系统服务监控。当 Android 检测到以下任一情况时,会显示应用的 ANR 对话框:
- 在 5 秒内未对输入事件(例如按键或屏幕点按事件)做出响应。
- 对于前台 Intent,
BroadcastReceiver
未在 10 到 20 秒内完成执行。如需了解详情,请参阅广播接收器超时。
避免 ANR
以下是避免 ANR 的一般提示。如需详细了解如何诊断和调试不同类型的 ANR,请参阅本部分的其余页面。
始终保持主线程不被阻塞,并策略性地使用线程。
不要在应用的主线程上执行阻塞或长时间运行的操作。而是创建工作线程并在那里完成大部分工作。
尽量减少主线程与其他线程之间的任何锁竞争。
尽量减少主线程上的任何非 UI 相关工作,例如处理广播或运行服务时。在 UI 线程中运行的任何方法都必须在该线程上做尽可能少的工作。特别是,Activity 在关键生命周期方法(例如
onCreate()
和onResume()
)中进行设置时,必须做尽可能少的工作。如需了解有关在后台线程上安排工作并与 UI 通信的可用解决方案的更多信息,请参阅后台工作概览。在组件之间共享线程池时要小心。不要将相同的线程用于可能长时间阻塞的操作和时间敏感的任务(例如广播接收)。
保持应用快速启动。尽量减少应用启动代码中的缓慢或阻塞操作,例如在 Dagger 初始化期间运行的方法。
如果您正在使用
BroadcastReceiver
,请考虑使用Context.registerReceiver
在非主线程中运行广播接收器。如需了解详情,请参阅BroadcastReceiver 中的 ANR。- 如果您使用
goAsync()
,请确保在 ANR 超时之前快速调用PendingResult.finish
。
- 如果您使用
BroadcastReceiver 中的 ANR
BroadcastReceiver
执行时间受限,因为广播接收器旨在后台执行少量离散工作,例如保存设置或注册Notification
。因此,与在 UI 线程中调用的其他方法一样,应用必须避免在广播接收器中执行可能长时间运行的操作或计算。与其通过 UI 线程执行长时间运行的任务,不如在后台执行它们以供后续执行。如需了解有关可能解决方案的更多信息,请参阅后台工作概览。
BroadcastReceiver
对象的另一个常见问题是它们执行过于频繁。频繁的后台执行会减少其他应用的可用内存量。如需了解有关如何高效启用和禁用 BroadcastReceiver
对象的更多信息,请参阅广播概览。
增强响应能力
通常,100 到 200 毫秒是用户感知应用卡顿的阈值。以下是让您的应用对用户看起来响应更快的其他提示:
如果您的应用正在后台响应用户输入执行工作,请显示正在进行的进度,例如在 UI 中使用
ProgressBar
。特别是对于游戏,请在工作线程中进行移动计算。
如果您的应用具有耗时的初始设置阶段,请考虑显示启动画面或尽快渲染主视图。指示正在加载并异步填充信息。在任何一种情况下,我们都建议以某种方式指示正在进行进度,以便用户不会认为应用已冻结。
使用 Perfetto 和 CPU Profiler 等性能工具来确定应用响应能力中的瓶颈。