Android ANR 排查实战:主线程阻塞、StrictMode 与 Trace 还原卡点

作者: Android学习网 分类: Android平台开发 发布时间: 2026-03-25 11:43

## 问题是什么

线上偶发 ANR,日志只看到:

Input dispatching timed out
Broadcast of Intent
Executing service timeout

这类问题不要先猜框架。先确认一件事:**主线程到底被谁卡住了**。

## 先做最小定位

第一步,先把主线程上的危险动作亮出来。

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        StrictMode.setThreadPolicy(
            StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()
                .detectDiskWrites()
                .detectNetwork()
                .penaltyLog()
                .build()
        )
    }
}

如果一开 StrictMode 就刷日志,先别优化别的,先把主线程 IO 清掉。

## 常见卡点

1、主线程读数据库或文件

val json = assets.open("config.json").bufferedReader().use { it.readText() }
val user = userDao.queryById(uid)

2、主线程直接请求网络

val response = okHttpClient.newCall(request).execute()

3、`onCreate()` 里堆 SDK 初始化

override fun onCreate() {
    super.onCreate()
    Analytics.init(this)
    PushSdk.init(this)
    CrashSdk.init(this)
    MapSdk.init(this)
}

4、列表首帧前同步解析大对象

val bigList = gson.fromJson(json, type)
adapter.submitList(bigList)

## 怎么看 trace

发生 ANR 后先拉:

adb shell ls /data/anr
adb pull /data/anr/anr_* ./anr_dump

看主线程堆栈,重点看:

main
 at xxx
 at xxx
 locked <...>
 waiting to lock <...>

判断顺序:

– 卡在 `Binder`:先怀疑系统调用/跨进程
– 卡在 `monitor`:先怀疑锁竞争
– 卡在磁盘/数据库:先怀疑主线程 IO
– 卡在 JSON/Bitmap:先怀疑大对象解析或图片处理

## 一种常见修法

把重活挪出主线程,但别瞎扔线程,统一进 IO 或 Default。

class UserRepository(
    private val userDao: UserDao,
    private val api: UserApi
) {
    suspend fun loadUser(uid: Long): User = withContext(Dispatchers.IO) {
        userDao.queryById(uid) ?: api.fetchUser(uid).also { userDao.insert(it) }
    }
}

页面只收结果:

viewModelScope.launch {
    val user = repository.loadUser(uid)
    binding.name.text = user.name
}

## SDK 初始化怎么收

不要全塞 `Application.onCreate()`。

override fun onCreate() {
    super.onCreate()
    initCriticalOnly()
    ProcessLifecycleOwner.get().lifecycle.addObserver(AppInitObserver())
}

private fun initCriticalOnly() {
    CrashSdk.init(this)
}

class AppInitObserver : DefaultLifecycleObserver {
    override fun onStart(owner: LifecycleOwner) {
        Analytics.init(app)
        PushSdk.init(app)
    }
}

## 怎么验证修好了

1、看 StrictMode 日志是否消失

adb logcat | grep StrictMode

2、模拟卡顿场景重复点页面、切后台、回前台

3、对比修复前后主线程耗时埋点

val start = SystemClock.elapsedRealtime()
block()
Log.d("MainCost", "cost=${SystemClock.elapsedRealtime() - start}")

4、再看 ANR 监控是否下降

## 这篇记住 3 点

– **先看主线程卡在哪,不要先猜框架**
– **先开 StrictMode,最快暴露主线程脏活**
– **修完一定回看 trace 和日志,不凭感觉说解决了**

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注