保持应用响应

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

本文档介绍了 Android 系统如何确定应用是否未响应,并说明了如何保持应用响应。

无论您的代码编写得多么好,您的应用都可能仍然感觉迟钝、挂起、冻结较长时间或处理输入花费的时间过长。如果您的应用处于前台且无响应,则用户会收到“应用程序无响应 (ANR)”对话框,如图 1 所示。ANR 对话框允许用户强制退出应用。如果应用不在前台,则会静默停止。至关重要的是,在应用中设计响应能力以最大程度地减少 ANR 对话框。

ANR 触发器

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

例如,如果应用在 UI 线程上执行阻塞式 I/O 操作(例如网络访问),则可能会发生 ANR。另一个示例是,当应用在 UI 线程上花费太多时间构建复杂的内存中结构或计算游戏中下一步操作时。

在 Android 中,应用响应能力由 ActivityManagerWindowManager 系统服务监控。当检测到以下任一条件时,Android 会为应用显示 ANR 对话框

  • 在 5 秒内没有响应输入事件(例如按键或屏幕点击事件)。
  • 一个 BroadcastReceiver 未在 10 到 20 秒内完成执行(对于前台意图)。有关更多信息,请参阅 广播接收器超时

避免 ANR

以下是一些避免 ANR 的一般技巧。有关诊断和调试不同类型 ANR 的更多详细信息,请参阅本节中的其他页面。

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

    • 不要在应用的主线程上执行阻塞或长时间运行的操作。相反,创建一个工作线程并在那里完成大部分工作。

    • 尝试最大程度地减少主线程和其他线程之间的任何锁争用。

    • 尽量减少主线程上与 UI 无关的工作,例如处理广播或运行服务时。在 UI 线程中运行的任何方法都必须尽可能少地在此线程上执行工作。特别是,活动必须尽可能少地在关键生命周期方法(例如 onCreate()onResume())中进行设置。有关在后台线程上调度工作并与 UI 通信的可用解决方案的更多信息,请参阅后台工作概述

    • 在组件之间共享线程池时要小心。不要对可能长时间阻塞的操作和时间敏感的任务(例如广播接收)使用相同的线程。

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

  • 如果您使用的是 BroadcastReceiver,请考虑使用 Context.registerReceiver 在非主线程中运行广播接收器。有关更多信息,请参阅广播接收器中的 ANR

广播接收器中的 ANR

BroadcastReceiver 的执行时间受到限制,因为广播接收器旨在在后台执行少量、离散的工作,例如保存设置或注册 Notification。因此,与在 UI 线程中调用的其他方法一样,应用必须避免在广播接收器中执行可能长时间运行的操作或计算。不要通过 UI 线程执行长时间运行的任务,而应在后台执行以供稍后执行。有关可能解决方案的更多信息,请参阅后台工作概述

另一个常见的 BroadcastReceiver 对象问题是它们执行过于频繁。频繁的后台执行会减少其他应用可用的内存量。有关如何有效启用和禁用 BroadcastReceiver 对象的更多信息,请参阅广播概述

增强响应能力

通常,100 到 200 毫秒是用户感知应用缓慢的阈值。以下是一些使应用对用户看起来更具响应性的其他技巧

  • 如果您的应用正在后台执行响应用户输入的工作,请显示正在进行的进度,例如在 UI 中使用 ProgressBar

  • 对于游戏而言,请在工作线程中计算移动。

  • 如果您的应用具有耗时的初始设置阶段,请考虑显示 启动画面 或尽快呈现主视图。指示加载正在进行并异步填充信息。在任何一种情况下,我们都建议以某种方式指示正在进行的进度,以便用户不会认为应用已冻结。

  • 使用性能工具(例如 PerfettoCPU Profiler)来确定应用响应能力的瓶颈。