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 问题,提升应用性能和用户体验

相关推荐
阿巴斯甜12 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker13 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952714 小时前
Andorid Google 登录接入文档
android
黄林晴15 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android