Android ANR 排查实战:主线程阻塞、StrictMode 与 Trace 还原卡点
## 问题是什么
线上偶发 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 和日志,不凭感觉说解决了**
