news 2026/7/17 1:15:23

Android随笔-ANR

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 的本质是主线程被阻塞,预防的核心就是不要让主线程干重活——耗时操作全部异步化,同步依赖全部回调化。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/17 1:12:47

Java发送HTTP GET请求的5种高效方法

在Java里头, 发送HTTP GET请求主要存在几种方式: 运用.、运用 、运用 库, 又或者运用Java 11当中引入的.http.。这些方式全都能够达成在Java里创建和发送HTTP GET请求的要求, 然而它们于易用性、功能性以及可维护性方面存有差别。在这些方法里头, .http. 于Java 11的时候被引进…

作者头像 李华
网站建设 2026/7/17 1:11:50

Windows权限错误0x80070005解析与解决方案

1. 错误代码0x80070005问题解析当Windows系统提示"无法设置默认保存位置"并伴随0x80070005错误代码时&#xff0c;这通常意味着系统遇到了权限问题。这个特定错误代码在Windows生态中表示"访问被拒绝"(Access Denied)&#xff0c;是系统安全机制阻止了某项…

作者头像 李华
网站建设 2026/7/17 1:10:52

Windows 10 WMI Provider Host进程CPU占用过高解决方案

1. 问题现象与初步诊断当Windows 10系统的WMI Provider Host进程&#xff08;WmiPrvSE.exe&#xff09;出现CPU占用过高时&#xff0c;通常表现为系统响应变慢、风扇转速加快、任务管理器显示该进程持续占用25%以上的CPU资源。这种情况往往发生在系统后台执行管理任务时&#x…

作者头像 李华
网站建设 2026/7/17 1:04:19

什么是谷歌SEO?从0到1带你拆解价值百万的独立站流量密码!

在做海外市场、跨境电商或者个人独立站的朋友&#xff0c;你一定听过这样一个词&#xff1a;谷歌 SEO。 很多人一听到“SEO”这种技术词汇就头大&#xff0c;觉得这是程序员才懂的神秘代码。但实际上&#xff0c;SEO 是一门高级的“用户心理学”和“内容编排术”。 如果你不想…

作者头像 李华
网站建设 2026/7/17 0:36:10

鸿蒙原生开发手记:徒步迹 - 首页布局设计与Scroll滚动

鸿蒙原生开发手记&#xff1a;徒步迹 - 首页布局设计与Scroll滚动使用 Scroll 和 ForEach 构建可滚动的首页一、前言 首页是 App 的门面&#xff0c;需要展示丰富的信息同时保持良好的浏览体验。徒步迹的首页包括&#xff1a;今日概览、快捷功能入口、推荐路线三大模块。 本文详…

作者头像 李华