让应用能迅速响应

图 1. 向用户显示的 ANR 对话框。

本文档介绍了 Android 系统如何确定应用是否无响应,并介绍了如何让应用保持迅速响应。

无论代码编写得有多好,应用仍可能会出现运行缓慢、挂起、卡住较长时间或处理输入花费太长的情况。如果应用在前台运行且无响应,用户会看到“应用无响应”(ANR) 对话框,如图 1 所示。ANR 对话框可让用户强制退出应用。如果应用未在前台运行,则会静默停止。在应用中设计响应能力以最大限度地减少 ANR 对话框至关重要。

ANR 触发器

通常,如果应用无法在主线程(也称为界面线程)上响应用户输入,系统会显示 ANR,从而阻止系统处理传入的用户输入事件。

例如,如果应用在界面线程上执行阻塞型 I/O 操作(例如网络访问),就可能会发生 ANR。再比如,应用花费太多时间在界面线程上构建复杂的内存结构或计算游戏中的下一个走法。

在 Android 中,应用响应速度由 ActivityManagerWindowManager 系统服务监控。检测到以下情况之一时,Android 会显示应用的 ANR 对话框:

  • 在 5 秒内对输入事件(例如按键或屏幕点按事件)没有响应。
  • 对于前台 intent,BroadcastReceiver 未在 10 到 20 秒内完成执行。如需了解详情,请参阅广播接收器超时

避免 ANR

以下是避免 ANR 的一般提示。如需详细了解如何诊断和调试不同类型的 ANR,请参阅本部分的其他页面。

  • 始终保持主线程畅通,并有策略地使用线程。

    • 请勿在应用的主线程上执行阻塞或长时间运行的操作。 而应创建一个工作器线程并在其中完成大部分工作。

    • 尽量减少主线程和其他线程之间发生的任何锁争用。

    • 最大限度地减少主线程上任何与界面无关的工作,例如处理广播或运行服务时的工作。在界面线程中运行的任何方法都必须尽可能少地在该线程中执行工作。具体而言,activity 必须尽可能少地在关键生命周期方法(例如 onCreate()onResume())中设置 activity。如需详细了解用于在后台线程上调度工作以及与界面回通信的可用解决方案,请参阅后台工作概览

    • 在组件之间共享线程池时要小心。不要使用相同的线程来处理可能耗时较长的操作和时效性任务(如广播接收)。

  • 确保应用快速启动。尽量减少在应用的启动代码中执行缓慢或阻塞操作的操作,例如在 dagger 初始化期间运行的方法。

  • 如果您使用的是 BroadcastReceiver,不妨考虑使用 Context.registerReceiver 在非主线程中运行广播接收器。如需了解详情,请参阅 BroadcastReceiver 中的 ANR

BroadcastReceiver 中的 ANR

BroadcastReceiver 的执行时间受限,因为广播接收器旨在在后台执行少量离散工作,例如保存设置或注册 Notification。因此,与界面线程中调用的其他方法一样,应用必须避免广播接收器中可能长时间运行的操作或计算。与其通过界面线程执行长时间运行的任务,不如在后台执行这些任务以便稍后执行。如需详细了解可能的解决方案,请参阅后台工作概览

BroadcastReceiver 对象的另一个常见问题会在其执行过于频繁时发生。频繁的后台执行可能会减少其他应用可用的内存量。如需详细了解如何高效地启用和停用 BroadcastReceiver 对象,请参阅广播概览

加强响应能力

通常,100 到 200 毫秒是一个阈值,超出该阈值后,用户会感觉应用运行缓慢。下面列出了更多提示,可帮助您了解如何让应用能够迅速响应用户:

  • 如果应用在后台执行操作以响应用户输入,请表明正在进行工作,例如在界面中使用 ProgressBar

  • 尤其是对于游戏,在工作器线程中计算移动次数。

  • 如果应用的初始设置阶段非常耗时,请考虑显示启动画面或尽快呈现主视图。指示正在加载并异步填充信息。无论是哪种情况,我们都建议您以某种方式表明相应进度正在进行,这样用户就不会感知到应用被卡住了。

  • 使用 PerfettoCPU 性能分析器等性能工具确定应用响应能力方面的瓶颈。