跳到主要内容

ANR 分析与优化

问题

ANR 是什么?如何排查和避免 ANR?

答案

ANR 触发条件

类型超时时间说明
Input 事件5 秒按键或触摸事件未响应
BroadcastReceiver前台 10s / 后台 60sonReceive 未完成
Service前台 20s / 后台 200sonCreate/onStartCommand 未完成
ContentProvider10 秒publish 阶段超时

常见原因

排查方法

1. traces.txt 分析

# 获取 ANR 日志
adb pull /data/anr/traces.txt

# 或在 Android 11+ 使用
adb bugreport bugreport.zip

traces.txt 关键信息:

----- pid 12345 at 2024-01-01 12:00:00 -----
"main" prio=5 tid=1 Blocked
| group="main" sCount=1 dsCount=0
| held mutexes=
at com.example.db.UserDao.queryAll(UserDao.java:42)
- waiting to lock <0x12345678> (a java.lang.Object) held by thread 15
at com.example.ui.MainActivity.onCreate(MainActivity.java:28)

关键看点

  • 主线程状态(BlockedWaitingRunnable
  • 被阻塞的位置(堆栈)
  • 锁被哪个线程持有

2. Perfetto 分析

# 对于 ANR,Perfetto 可以看到主线程被阻塞的时间段
# 在 Perfetto UI 中搜索 "ANR" 标签

3. StrictMode 提前发现

if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog() // 日志输出
.penaltyDeath() // 直接崩溃(激进)
.build()
)
}

解决方案

// ❌ 主线程操作数据库
fun loadData() {
val users = db.userDao().getAll() // ANR 风险!
adapter.submitList(users)
}

// ✅ 协程 + IO 调度
fun loadData() {
lifecycleScope.launch {
val users = withContext(Dispatchers.IO) {
db.userDao().getAll()
}
adapter.submitList(users)
}
}

常见面试问题

Q1: 如何在线上监控 ANR?

答案

  1. ANR WatchDog:独立线程定期向主线程 post 消息,超时未响应则上报
  2. Firebase Performance:自动收集 ANR 数据
  3. Perfetto:Android 11+ 系统自动记录 ANR trace
  4. 自定义监控:通过 Choreographer.FrameCallback 检测主线程卡顿
class ANRWatchDog : Thread() {
private val mainHandler = Handler(Looper.getMainLooper())
@Volatile private var tick = 0L

override fun run() {
while (!isInterrupted) {
tick = System.currentTimeMillis()
mainHandler.post { tick = 0L }
Thread.sleep(5000)
if (tick != 0L) {
// 主线程 5 秒未响应,上报 ANR
reportANR()
}
}
}
}

Q2: BroadcastReceiver 中耗时操作怎么处理?

答案

使用 goAsync() 延长处理时间(但仍有 30s 限制),或启动 WorkManager 任务:

class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val pendingResult = goAsync()
CoroutineScope(Dispatchers.IO).launch {
doHeavyWork()
pendingResult.finish()
}
}
}

相关链接