当前位置: 首页 > news >正文

Android随笔-ANR

一、ANR 是什么

ANR(Application Not Responding,应用无响应)是 Android 系统的一种自我保护机制:当主线程(UI Thread)在规定时间内无法响应用户输入或系统事件时,系统会弹出"应用无响应"对话框,提示用户选择"等待"或"关闭应用"。

本质上,ANR 是主线程被阻塞,导致消息队列(MessageQueue)中的事件无法被及时处理。


二、ANR 的判定标准(超时阈值)

场景超时时间触发条件
输入事件 ANR5 秒按键、触摸等输入事件未被处理
Broadcast ANR10 秒(前台)/ 60 秒(后台)onReceive()执行超时
Service ANR20 秒onCreate()/onStartCommand()执行超时
ContentProvider ANR10 秒onCreate()执行超时

注意:这些超时时间是硬编码在系统源码中的,应用层无法修改。


三、哪些场景会产生 ANR

1. 主线程执行耗时操作(最常见)

// ❌ 错误示例:主线程直接网络请求@OverridepublicvoidonClick(Viewv){// 阻塞主线程,超过5秒就会ANRStringresult=HttpUtils.get("https://api.example.com/data");textView.setText(result);}

其他常见耗时操作:

  • 网络请求(HTTP、Socket)
  • 数据库读写(SQLite)
  • 文件 I/O(读写大文件、SharedPreferences 的commit()
  • 大量数据计算(复杂算法、图片编解码)
  • sleep()wait()长时间阻塞

2. 主线程死锁

// ❌ 错误示例:主线程死锁privatefinalObjectlockA=newObject();privatefinalObjectlockB=newObject();// 线程1newThread(()->{synchronized(lockA){try{Thread.sleep(100);}catch(Exceptione){}synchronized(lockB){/* do something */}}}).start();// 主线程publicvoidonClick(Viewv){synchronized(lockB){synchronized(lockA){// 死锁!主线程永远等不到lockAtextView.setText("done");}}}

3. BroadcastReceiver 中执行耗时操作

// ❌ 错误示例:在 onReceive 中做耗时操作publicclassMyReceiverextendsBroadcastReceiver{@OverridepublicvoidonReceive(Contextcontext,Intentintent){// 这里超过10秒就会ANRprocessLargeData();// 模拟耗时操作}}

4. Service 的 onCreate / onStartCommand 耗时

// ❌ 错误示例:Service启动时初始化过多publicclassMyServiceextendsService{@OverridepublicintonStartCommand(Intentintent,intflags,intstartId){// 超过20秒ANRinitDatabase();// 耗时loadConfig();// 耗时syncAllData();// 耗时returnSTART_STICKY;}}

5. 主线程同步等待子线程结果

// ❌ 错误示例:主线程阻塞等待子线程publicvoidonClick(Viewv){finalString[]result=newString[1];Threadthread=newThread(()->{result[0]=doHeavyWork();});thread.start();thread.join();// 主线程阻塞等待!如果子线程超过5秒,ANRtextView.setText(result[0]);}

6. 过度绘制或布局层级过深(间接导致)

虽然严格来说不是直接阻塞主线程,但过于复杂的布局会导致onMeasure/onLayout/onDraw耗时过长,在低端设备上可能触发输入事件超时。


四、ANR 的检测与诊断

1. 查看 traces.txt

ANR 发生时,系统会生成/data/anr/traces.txt,包含所有线程的堆栈信息。

adb pull /data/anr/traces.txt

关键信息:

  • main线程的状态(RUNNABLEBLOCKEDWAITING
  • 阻塞在哪个方法、哪行代码
  • 锁的持有者和等待者

2. 使用 StrictMode

// 开发阶段检测主线程违规操作publicclassMyAppextendsApplication{@OverridepublicvoidonCreate(){super.onCreate();StrictMode.setThreadPolicy(newStrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog()// 只打印日志,不上报.build());}}

3. 使用 Systrace / Perfetto

抓取系统级 trace,分析主线程在每个阶段的耗时。


五、如何预防 ANR

1. 耗时操作放到子线程(核心原则)

// ✅ 正确示例:使用 Thread + HandlerpublicvoidonClick(Viewv){newThread(()->{finalStringresult=HttpUtils.get("https://api.example.com/data");runOnUiThread(()->textView.setText(result));}).start();}

2. 使用 AsyncTask(已废弃,了解即可)

// AsyncTask 在 Android 11 已废弃,建议用协程或线程池替代

3. 使用 Kotlin 协程(推荐)

// ✅ 推荐:Kotlin 协程lifecycleScope.launch{valresult=withContext(Dispatchers.IO){// 在 IO 线程执行耗时操作repository.fetchData()}// 自动切回主线程更新 UItextView.text=result}

4. 使用线程池

// ✅ 正确示例:使用线程池ExecutorServiceexecutor=Executors.newFixedThreadPool(4);publicvoidonClick(Viewv){executor.execute(()->{finalStringresult=doHeavyWork();runOnUiThread(()->textView.setText(result));});}

5. BroadcastReceiver 中使用 goAsync()

// ✅ 正确示例:异步处理广播publicclassMyReceiverextendsBroadcastReceiver{@OverridepublicvoidonReceive(Contextcontext,Intentintent){finalPendingResultpendingResult=goAsync();newThread(()->{try{processLargeData();}finally{pendingResult.finish();// 必须调用,否则系统会ANR}}).start();}}

6. Service 中使用 IntentService / WorkManager

// ✅ 正确示例:IntentService(已废弃,建议用 WorkManager)// 或使用 JobIntentService / WorkManager 处理后台任务

7. 避免主线程同步锁

// ✅ 正确示例:使用回调代替阻塞等待publicvoidonClick(Viewv){fetchDataAsync(newCallback<String>(){@OverridepublicvoidonResult(Stringresult){runOnUiThread(()->textView.setText(result));}});}

8. 优化布局性能

  • 减少布局层级(使用 ConstraintLayout)
  • 避免过度绘制
  • 使用ViewStub延迟加载
  • 避免在onDraw中创建对象

六、总结:ANR 预防 checklist

场景预防措施
网络请求必须用子线程/协程
数据库操作用 Room + 协程,或子线程
文件 I/O子线程,SharedPreferences 用apply()代替commit()
复杂计算子线程,必要时用AsyncTask替代品
同步等待用回调、Future、协程替代join()/wait()
广播接收goAsync()或启动 Service 处理
Service 启动避免在onCreate中做耗时初始化
布局优化减少层级,避免过度绘制

一句话总结:ANR 的本质是主线程被阻塞,预防的核心就是不要让主线程干重活——耗时操作全部异步化,同步依赖全部回调化。

http://www.jsqmd.com/news/1203533/

相关文章:

  • Windows 10 Build 10064技术预览版安装与优化指南
  • 百达翡丽中国官方售后服务中心|最新电话和完整维修地址权威信息通知(2026年7月更新) - 百达翡丽官方售后中心
  • 2026年四川地区理赔法律服务诚信品牌深度解析与选择指南 - 品牌鉴赏官2026
  • 广州百达翡丽回收价格查询与靠谱回收平台实测排行(2026年7月最新数据) - 嘉价奢侈品回收平台
  • 评标现场的“数字守护者”:镜像视界视频孪生,如何穿透迷雾,实现精准管控
  • 美度中国官方售后服务中心|服务热线与门店详细地址权威信息公告(2026年7月更新) - 亨得利官方服务中心
  • H5GG终极指南:如何用JavaScript轻松修改iOS游戏内存
  • 泰格豪雅中国官方售后服务中心|服务电话及全部维修地址权威信息公示(2026年7月更新) - 亨得利官方服务中心
  • 2026年近期大型圣诞树批发商专业甄选指南:如何找到供应商 - 品牌鉴赏官2026
  • Java发送HTTP GET请求的5种高效方法
  • 深圳夏令营口碑推荐:军博营地声名远扬 - 云溪自乐
  • 室内卡丁车馆如何破解低频消费获客难?餐宝盈小程序+GEO模式详解,含零代码SAAS、AI编程、源码定制
  • 宝玑中国官方售后服务中心|服务热线及网点地址权威信息通告(2026年7月更新) - 亨得利钟表维修中心
  • 重庆百达翡丽回收价格查询及靠谱平台实测排行(2026年7月最新数据) - 尊奢回收二奢平台
  • 亲身探访重庆万国官方售后服务中心|官方地址及售后服务热线(2026年7月最新) - 万国中国官方服务中心
  • 广州夏令营哪家专业:军博营地教学精湛 - 晚香时候
  • Claude批量预处理全链路优化:JSON Schema校验+异步队列+结果归一化(含GitHub开源模板)
  • Windows 11运行框的6种打开方式与高效使用技巧
  • 深圳夏令营短期班推荐:军博营地速成见效 - 晴光转树
  • JetBrains IDE评估重置机制技术实现深度解析
  • OpenHarmony NAPI开发:数据类型映射与性能优化实践
  • 少儿编程培训如何提高试听到报名转化?餐宝盈小程序与GEO服务联动,含零代码SAAS、AI编程、源码定制
  • 2026年7月最新青岛浪琴官方售后客户服务热线与维修网点地址汇总 - 浪琴官方售后服务中心
  • 【DeepSeek实战速成指南】:20年AI工程师亲授,7天掌握模型调用、微调与部署全流程
  • 2026年北京旧房改造/厨卫翻新/老房翻新/全屋局部改造推荐榜单:匠心工艺与口碑服务深度解析 - 甄选服务推荐
  • Windows 10 WMI Provider Host进程CPU占用过高解决方案
  • Windows 98文件权限错误解析与解决方案
  • RAG、Agent、微调、私有化部署:大模型应用落地秘籍,小白程序员必备收藏!
  • 2026年7月亲身到店体验东莞亨得利官方名表服务中心|官方地址及联系电话 - 亨得利官方
  • 设计决策结构化:Lovart+Skills如何实现1秒调用10年经验