本文档介绍了 Android 系统如何确定应用是否没有作出响应,以及如何让应用保持快速响应。
无论代码编写得有多好,应用仍可能会出现运行缓慢、挂起、卡住较长时间或花费太长时间来处理输入的情况。如果应用在前台运行且无响应,则系统会显示“应用无响应”(ANR) 对话框,如图 1 所示。ANR 对话框可让用户强制退出应用。如果应用不在前台运行,则会静默停止。在设计应用时应充分考虑响应能力,以最大限度地减少 ANR 对话框的出现,这一点至关重要。
ANR 触发器
通常,如果应用无法在主线程(也称为界面线程)中响应用户输入,系统会显示 ANR,从而阻止系统处理传入的用户输入事件。
例如,如果应用在界面线程中执行阻塞 I/O 操作(例如网络访问),就可能会发生 ANR。另一个例子是,应用在界面线程中花费太多时间构建复杂的内存中结构或计算游戏的下一个走法。
在 Android 中,应用响应能力由 ActivityManager
和 WindowManager
系统服务监控。当系统检测到以下任一情况时,Android 会为应用显示 ANR 对话框:
- 在 5 秒内对输入事件(例如按键或屏幕点按事件)没有响应。
- 对于前台 intent,
BroadcastReceiver
未在 10 到 20 秒内执行完毕。如需了解详情,请参阅广播接收器超时。
避免出现 ANR
以下是避免出现 ANR 的一般提示。如需详细了解如何诊断和调试不同类型的 ANR,请参阅本部分的其他页面。
始终保持主线程畅通,并有策略地使用线程。
请勿在应用的主线程中执行阻塞操作或长时间运行的操作。 而是要创建工作器线程并在其中执行大部分操作。
尝试尽量减少在主线程和其他线程之间发生任何锁争用。
尽量减少在主线程中执行任何与界面无关的工作;例如,处理广播或运行服务时的工作。在界面线程中运行的任何方法都必须尽可能减少在该线程中执行工作。特别是在
onCreate()
和onResume()
等关键生命周期方法中,activity 必须尽量减少进行设置所需的操作。如需详细了解有关在后台线程中调度工作以及将结果传回界面的可用解决方案,请参阅后台工作概览。在组件之间共享线程池时要小心。不要使用相同的线程来处理可能耗时较长的操作和有时效性的任务(如广播接收)。
确保应用快速启动。尽量减少在应用的启动代码中执行缓慢的操作或阻塞操作,例如在 dagger 初始化期间运行的方法。
如果您使用的是
BroadcastReceiver
,不妨考虑使用Context.registerReceiver
在非主线程中运行广播接收器。如需了解详情,请参阅 BroadcastReceiver 中的 ANR。- 如果您使用
goAsync()
,请确保在 ANR 超时之前快速调用PendingResult.finish
。
- 如果您使用
BroadcastReceiver 中的 ANR
对 BroadcastReceiver
的执行时间是有约束的,因为广播接收器的目的是在后台执行少量离散工作,例如保存设置或注册 Notification
。因此,与在界面线程中调用的其他方法一样,应用必须避免在广播接收器中执行可能会长时间运行的操作或计算。与其通过界面线程执行长时间运行的任务,不如在后台执行这些任务以便稍后执行。如需详细了解可能的解决方案,请参阅后台工作概览。
BroadcastReceiver
对象的另一个常见问题会在其频繁执行时出现。频繁在后台执行这些对象可能会减少其他应用可用的内存量。如需详细了解如何高效地启用和停用 BroadcastReceiver
对象,请参阅广播概览。
加强响应能力
通常,100 到 200 毫秒是一个阈值,一旦超出此阈值,用户会感觉应用运行缓慢。下面提供了更多技巧,可以让用户感觉您的应用响应迅速:
如果应用在后台执行操作来响应用户输入,则显示正在进行该操作,例如在界面中使用
ProgressBar
。特别是游戏,在工作器线程中计算走法。
如果应用的初始设置阶段非常耗时,请考虑显示启动画面或尽快呈现主视图,表明正在加载并异步填充信息。无论是哪种情况,我们都建议您以某种方式表明操作正在进行,以免用户认为应用已卡住。