直接回答
预防 Android ANR,首先要用证据判断系统为何认为某个组件无响应。先分类事件:输入分发 ANR 表示输入事件未及时处理;广播接收器、服务执行、内容提供者或 JobService ANR 则有不同触发条件和回调边界。适用的超时取决于 ANR 类型、平台版本、设备和系统状态。Android 文档举出部分默认值,但也说明 AOSP 与 Pixel 的时间可能因 OEM 不同而变化。不能把某个示例当成端侧推理的通用期限,也不能因为模型任务超过臆定秒数就认定它有缺陷。 对于输入分发 ANR,要检查相关时刻主线程正在做什么。若栈停在文件 I/O、同步 Binder 调用、数据库访问、初始化或等待锁,后续检查方向不同。主线程等锁时还要查锁持有线程:它是在运行、等待其他资源,还是没有得到调度。因此即使用户看到的是主线程卡住,后台线程也可能是 ANR 证据的一部分。把调用移出主线程还不够;如果 UI 立即同步等待该工作线程,或仍竞争同一把锁,无响应问题依旧存在。 优先读取实际可用的 ANR 签名、报告和 trace。Android 建议在 Perfetto 中查看主线程当时处于运行还是 runnable 状态,并检查 system_server 线程中的锁竞争或慢 Binder 回复。单独的主线程栈可能不完整,也可能是在因果事件之后才抓取。“nativePollOnce”或“main thread idle”对某些输入分发报告可能没有帮助,但其他 ANR 类型仍可能通过相关工作线程或接收器栈定位。签名只是入口,应结合事件类型、组件、进程、时间和复现步骤判断。 Android API 30 及以上的 ApplicationExitInfo 可以报告 REASON_ANR 等进程退出原因,并通过 getTraceInputStream 暴露系统 trace。它是进程退出后的证据,不是实时 ANR 监控,也不保证 trace 一定存在。Android 文档说明 trace 可能为空,例如被系统全局循环缓冲区中的较新 trace 覆盖。应按平台版本调用并处理数据缺失;没有退出历史或 trace 为空,不能作为“没有发生 ANR”的证明。更早版本或记录缺失时,使用该环境支持的系统报告或测试 trace。 要把 ANR 与其他慢或失败结果分开。模型初始化错误、低内存进程终止、native crash、后台推理较慢但仍有响应,以及 ANR 并不是同一件事。受支持的 ApplicationExitInfo 版本为 ANR、crash、low memory 等退出提供不同 reason code。正确放在 UI 线程之外的长任务可能是产品延迟或取消问题,但本身不是 ANR。反过来,模型调用即使很短,如果阻塞了必需的 UI 回调或持有主线程需要的锁,也可能引发 ANR。 对本地 AI 流程,要沿调用链核对主线程、ViewModel 或协调器、model manager、推理 worker、native runtime、持久化,以及回到主线程的回调。重点检查同步等待、worker 池饱和、持锁期间等待 UI 工作,或清理过程阻塞其他操作。模型加载与推理应有明确 owner,并异步更新状态;但这个通用架构原则本身不能证明 ANR 原因。只有报告或受控复现能支撑因果边界时,才选择相应修复。 仓库事实 COVE-LIFECYCLE-01 记录了 ModelManager 状态和串行加载;它不记录 Cove ANR 事故,也不表示 Cove App 已采集 ANR trace。BENCHMARK-01 还说明仓库缺少完整的真实设备基准数据。因此本文是诊断方法,不是 Cove 故障记录、响应时间结果或设备结论。除非另有审核过的支持流程明确要求,trace 附件和 bug 报告不得包含私密提示词、转录、图像或账号数据。 有依据的预防复验应在同一构建和环境下重复相同用户操作,且一次只做一个针对性改变。保留原始 ANR 类型与签名、线程栈或可用系统 trace、疑似等待或锁持有者,以及复验结果。trace 缺失时,应标注根因未确认并补充可复现采集,而不是从转圈动画推断原因。结论写明实际测试组件和剩余不确定性。不要用“所有模型调用都必须在固定五秒内完成”替代基于证据的诊断。
模型显示就绪,但首次点击后出现 ANR 对话框
测试人员打开模型已显示 ready 的本地 AI 功能,点击后 Android 报告应用无响应。安装和初始化都成功,但尚无 trace 说明主线程是否在预处理、等待推理线程的锁,或被其他组件阻塞。
决策
保存准确 ANR 类型和报告,检查主线程及锁持有者证据,再区分 ANR、后台慢推理与其他进程退出。
预期结果
只有 trace 或受控复现支持时才记录根因;证据不可用就标未确认并补采集,不归因于模型。
需要收集的证据
只有将以下证据与同一模型文件和测试运行关联起来,结论才可复核:
- COVE-LIFECYCLE-01:ModelManager 暴露模型加载状态并串行化模型加载,不记录 Cove ANR 事故。
- BENCHMARK-01:仓库无完整真实设备基准数据,本文只给出诊断方法,不提供性能结果。
只有记录了模型身份、构建配置、实际后端、任务样例和失败情况,运行时比较才可复核。
实施流程
- 分类 ANR 触发类型. 记录 subject、组件、进程、前后台状态、系统版本和时间。确认是输入、广播、服务、provider、job 还是其他类型,因为回调边界不同。
- 采集报告与退出记录. 保留系统或测试 ANR 报告及线程栈。API 30+ 检查 ApplicationExitInfo 和 trace stream;处理 null 与 IOException,并在后续退出记录附带旧 trace 时按进程和时间关联。
- 检查主线程和锁持有者. 定位阻塞 I/O、Binder、初始化或锁等待。如果 UI 线程等锁,检查持有者及其等待链;可用 Perfetto 区分运行、未调度、阻塞和竞争。
- 将证据映射到本地 AI 调用链. 把栈帧关联到 UI callback、ModelManager、native 调用、持久化和回调。只有 trace 或受控复现支持时,才把线程池饱和或锁循环记为根因。
- 单因修复并复验. 固定构建和输入,只改已确认的阻塞点或竞争边界,再复现相同操作。记录 trace 不可用或根因未确认的情况,并从附件中删除用户内容。
让每个阶段的状态都可观察。失败信息应指出阶段、模型、运行时和恢复动作,同时避免记录用户私密内容。
验收标准
- 报告写出 ANR 类型和组件,并区分 ANR、crash、low-memory exit、初始化失败及后台慢任务。
- 根因关联主线程栈、锁持有者或调度 trace,或可重复的受控复现;trace 缺失时明确标注 unavailable。
- 针对性改动后复验同一用户路径;没有证据时不宣称统一超时、Cove 事故、trace 采集功能或性能结果。
决策对照表
| 维度 | 选择或证据 | 解读方式 |
|---|---|---|
| 首屏就绪 | 检查应用外壳、模型状态和前置条件 | 发现启动问题,但不分类 ANR 或阻塞线程。 |
| 构建/运行故障排查 | 定位打包、安装、native load、模型加载或任务阶段 | 区分故障阶段,但解释无响应仍需要 ANR 报告和线程证据。 |
| ANR trace 诊断 | 关联事件类型、线程栈、锁持有者和调度 | 有证据时支持具体阻塞原因;缺 trace 时根因保持未确认。 |
| 长任务策略 | 设计异步 owner、进度和取消 | 改善任务管理,不等于 ANR 截止时间或 UI 阻塞证明。 |
需要避免的失败模式
- 把不同 ANR 类型都归因为模型推理太慢
- 只看主线程栈却忽略锁持有线程和调度状态
- 把 ApplicationExitInfo 没有 trace 当成没有 ANR
还要避免静默回退、版本未固定、缺少取消处理,以及把不同配置的结果混在一起下结论。失败运行也应与成功记录一并保存。
最小复现记录
| 层级 | 记录内容 |
|---|---|
| 设备 | 厂商、型号、芯片、内存档位、操作系统版本 |
| 软件 | 应用版本和 Git 提交号 |
| 模型 | 系列、变体、版本、格式、文件长度、摘要、量化方式 |
| 运行时 | 名称、版本、请求后端及实际后端证据 |
| 工作负载 | 样例版本、输入摘要、提示词摘要、输出策略 |
| 结果 | 完成、失败、取消、回退及保护隐私的诊断信息 |
发布检查表
- ☐ 主要搜索问题已回答,且没有未经支持的数字。
- ☐ 所有模型文件和运行时版本均已固定。
- ☐ 代表性任务和失败处理策略均有说明。
- ☐ 生命周期、取消、清理和回退均已测试。
- ☐ 用户内容与网络边界均有说明。
- ☐ 结论只适用于被记录的配置。
- ☐ 新增测试结果时链接到原始方法或证据。
来源与相关证据
中文部署与排错专题见。奇连 AI 端侧专题。
常见问题
所有 Android ANR 都意味着模型推理太慢吗?
不是。输入、广播、服务、provider、调度和锁竞争都可能触发 ANR。先看事件类型和相关栈,再判断是否涉及推理。
ApplicationExitInfo 一定能拿到 ANR trace 吗?
不能。该 API 从 API 30 起可用,trace 可能因循环缓冲区覆盖而为空,读取也可能抛 IOException。已恢复的 ANR trace 还可能附在后来因其他原因退出的记录上;应按进程和时间关联事件,不能仅凭退出 reason 或孤立栈定根因。
为了防止 ANR,所有本地模型请求都应设置五秒截止吗?
不应该。超时不是 ANR 诊断。异步任务可以运行较久而不阻塞 UI;较短调用也可能因争锁造成无响应。应依据事件和 trace 处理。
主线程栈空闲能证明应用没有问题吗?
不能。某些主线程栈可能采得较晚;同步屏障泄漏导致的 idle 症状在 Android 17 已修复,但 Android 16 及更早版本仍可能出现。应结合关联线程、组件上下文和系统 trace 判断,不能从 idle 栈单独定因。