android anr 处理

ANR(Application Not Responding)是 Android 中的一种常见问题。当应用程序未能在规定时间内响应用户输入、完成后台任务或处理系统消息时,系统会弹出一个对话框,提示"应用无响应"。这通常会严重影响用户体验。

在 Android 系统中,ANR 的触发主要有以下几种情境:

应用的主线程长时间阻塞,导致 UI 无法响应用户操作。

应用在处理特定类型的任务(如广播或服务)时未能在系统设定的时间内完成。

本文将结合 ANR 的源码逻辑和常见场景,深入解析 ANR 的三种主要类型及其解决方案。

一、ANR 的三种主要类型
1. Input Dispatching Timeout(输入事件分发超时)

问题描述

系统对用户输入事件(如点击、触摸等)的分发存在时限。默认情况下,主线程需要在 5 秒 内处理输入事件,否则会触发 ANR。

优化布局和绘制性能使用 ViewStub 或延迟加载减少布局复杂度。

2. BroadcastQueue Timeout(广播处理超时)

问题描述

广播接收器必须在指定时间内完成其任务:

前台广播:10 秒。

后台广播:60 秒。

如果超时,系统会认为应用发生了 ANR。

触发条件

在 onReceive() 方法中执行了耗时操作。

使用了复杂的广播逻辑或多次调用网络请求。

减少广播的频繁触发合理设计广播逻辑,避免不必要的广播。

使用 WorkManager 替代广播对于复杂任务,可以考虑使用 WorkManager,支持异步任务调度。

3. Service Timeout(服务处理超时)

问题描述

服务必须在规定时间内完成启动或执行任务:

前台服务:20 秒。

后台服务:200 秒。

触发条件

服务启动时进行复杂的初始化操作。

服务中执行了长时间未完成的任务。

二、ANR 的排查与分析

  1. 使用日志定位

在 logcat 中,ANR 相关日志通常以以下关键字开头:

"ANR in":发生 ANR 的应用包名。

"Reason":触发 ANR 的原因。

Reason: Input dispatching timed out (App did not respond to an input event within 5.0s)

Reason: Broadcast of Intent { act=android.intent.action.BOOT_COMPLETED }

Reason: executing service com.example.app/.MyService

"Load":设备负载情况(CPU 使用率等)。

示例:

ANR in com.example.app

PID: 12345

Reason: Input dispatching timed out (App did not respond to an input event within 5.0s)

Load: 5.68 / 4.23 / 2.11

  1. Trace 文件分析

trace.txt 文件是 Android 系统在发生 ANR 时自动生成的日志文件,记录了系统中所有线程的堆栈信息,特别是 主线程的状态。通过分析这个文件,开发者可以定位导致 ANR 的根本原因。以下是详细讲解分析 trace.txt 的步骤。

2.1 如何获取 trace.txt 文件
adb pull /data/anr/traces.txt

在 logcat 中标记文件生成路径每次发生 ANR 时,logcat 会记录 trace.txt 的路径。

在调试工具中直接查看部分集成开发环境(如 Android Studio)可以在运行时直接查看 ANR 信息。

2.2 trace.txt 的结构

trace.txt 文件主要包括以下内容:

线程信息每个线程的状态以线程名开头,如:

"main" prio=5 tid=1 Native

1

AI助手

"main":线程名称。主线程通常是 main。

prio=5:线程优先级。

tid=1:线程 ID。

Native/VM:线程运行环境。Native 表示本地代码,VM 表示虚拟机代码。

线程状态紧随线程信息的是其状态,如:

| state=RUNNABLE

1

AI助手

常见状态:

RUNNABLE:线程正在运行或等待 CPU 分配。

WAITING/TIMED_WAITING:线程等待某个条件或超时时间到达。

BLOCKED:线程被阻塞,通常因为锁争用。

堆栈信息每个线程的堆栈信息按调用顺序列出,从最顶层开始,如:

2.3 如何分析主线程的堆栈

定位主线程

主线程通常标识为:

"main" prio=5 tid=1 Native

主线程是 ANR 问题的重点,因为 UI 操作、输入事件、服务启动等都依赖于主线程。如果主线程被长时间阻塞,系统会认为应用无响应。

查找线程状态

主线程的状态直接决定了问题的方向:

RUNNABLE:线程在运行或等待 CPU。原因:可能是计算任务过于耗时或 CPU 资源不足。
WAITING:线程正在等待另一个线程的信号或资源。原因:通常是锁等待或阻塞操作。
BLOCKED:线程正在尝试获取另一个线程持有的锁。原因:锁争用或死锁问题。

分析堆栈信息

从主线程堆栈中,重点关注以下几点:

调用路径是否正常:是否存在循环调用或无意义的深层递归。

方法是否耗时:堆栈中是否包含 I/O 操作、网络请求、大量数据处理等。

第三方库问题:检查堆栈中是否包含第三方库的调用,它们可能引发性能问题。

2.4 示例分析

假设 trace.txt 文件中记录了以下主线程堆栈:

"main" prio=5 tid=1 Native

| state=WAITING

at java.lang.Object.wait(Native Method)

  • waiting on <0x12345> (a java.lang.Object)

at com.example.app.Worker.performTask(Worker.java:50)

  • locked <0x12345> (a java.lang.Object)

at com.example.app.MainActivity.onButtonClick(MainActivity.java:32)

at android.view.View.performClick(View.java:7125)

at android.view.View$PerformClick.run(View.java:28314)

at android.os.Handler.handleCallback(Handler.java:938)

at android.os.Handler.dispatchMessage(Handler.java:99)

at android.os.Looper.loop(Looper.java:268)

at android.app.ActivityThread.main(ActivityThread.java:7870)
state=WAITING 表明主线程正在等待另一个线程释放资源。

定位阻塞点

java.lang.Object.wait():主线程在等待一个 java.lang.Object 锁(对象 ID 为 <0x12345>)。

锁被 com.example.app.Worker.performTask 方法持有。

问题根源是 Worker.performTask() 中未能及时释放锁。

解决方法:优化 performTask 方法,确保锁释放及时或改用非阻塞式并发工具。

2.5 综合分析多个线程

ANR 问题可能涉及多个线程的交互。例如,主线程等待某个工作线程,而该工作线程又被其他线程阻塞。这种情况下,需要结合其他线程堆栈信息:

示例

"WorkerThread" prio=5 tid=12 Native

| state=BLOCKED

at com.example.app.DatabaseHelper.query(DatabaseHelper.java:120)

  • waiting to lock <0x54321&gt> (a java.lang.Object) held by tid=14

继续查找 tid=14 的堆栈信息,找到问题根源。

2.6 常见问题与解决策略

问题类型 表现 解决策略

主线程阻塞 堆栈显示主线程处于 WAITING/BLOCKED 检查锁争用,优化主线程任务逻辑。

后台线程卡死 辅助线程长时间运行,导致主线程等待 使用线程池或优化线程任务分配。

耗时操作未异步化 堆栈中出现 sleep() 或 I/O 操作 将耗时操作移至工作线程。

网络或数据库瓶颈 堆栈中显示网络或数据库方法长时间运行 优化查询逻辑或使用缓存。

  1. 使用 Systrace 或 Perfetto

通过 Systrace 或 Perfetto 工具分析应用的线程调度和性能瓶颈,定位主线程阻塞原因。后续文章会详细讲解Perfetto的相关知识。

三、总结

ANR 的本质是主线程未能在规定时间内完成预期任务或响应输入。

常见类型包括输入事件分发超时、广播处理超时和服务启动超时。

解决方案的核心是优化主线程逻辑,将耗时任务移至后台线程,并充分利用 Android 提供的异步工具(如 AsyncTask、HandlerThread 和 WorkManager)。

通过合理设计代码和使用调试工具,开发者可以有效避免和解决 ANR 问题,提升应用性能和用户体验

相关推荐
花追雨4 小时前
Android -- 双屏异显之方法一
android·双屏异显
小趴菜82274 小时前
安卓 自定义矢量图片控件 - 支持属性修改矢量图路径颜色
android
氤氲息4 小时前
Android v4和v7冲突
android
KdanMin4 小时前
高通Android 12 Launcher应用名称太长显示完整
android
chenjk44 小时前
Android不可擦除分区写文件恢复出厂设置,无法读写问题
android
袁震4 小时前
Android-Glide缓存机制
android·缓存·移动开发·glide
工程师老罗4 小时前
Android笔试面试题AI答之SQLite(2)
android·jvm·sqlite
User_undefined6 小时前
uniapp Native.js 调用安卓arr原生service
android·javascript·uni-app
刘争Stanley9 小时前
如何高效调试复杂布局?Layout Inspector 的 Toggle Deep Inspect 完全解析
android·kotlin·android 15·黑屏闪屏白屏