理解ANR的前世今生,解决ANR就是这么简单!

1. 什么是ANR?

ANR(Application Not Responding)即应用程序无响应。

2. ANR产生的原因是什么?

ANR产生的根本原因是APP阻塞了UI线程。在android系统中每个App只有一个UI线程,是在App创建时默认生成的,UI线程默认初始化了一个消息循环来处理UI消息,ANR往往就是处理UI消息超时了。那么UI消息来源有哪些呢?主要有两种来源:

2.1 来自于AMS的回调消息

在Android系统中,应用程序是有Android的四大组件组成,AMS负责对应用程序四大组件生命周期的管理,当AMS对应用程序组件的生命周期进行回调超过AMS定义的响应时间时,AMS就会报ANR。出现这种情况,一般是因为在这些组件的回调函数里面进行了耗时操作(如网络操作、SD卡文件操作、数据库操作、大量计算等),AMS对组件常见的回调函数及超时时间如下:

  • Activity: onCreate(), onResume(), onDestroy(), onKeyDown(), onClick()等,超时时间5s
  • Service: onCreate(), onStart(), onDestroy()等,超时时间20s
  • BroadcastReceiver:onReceiver(),前台APP广播超时时间是10s,后台App是60s

2.2 App自己的发出的消息

除了AMS对四大组件的回调消息运行在UI线程外,有些操作也是运行在UI线程的:

  • AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel()等,超时5s
  • Mainthread handler: handleMessage(), post*(runnable r)等,超时5s

3. 如何避免ANR?

知道了ANR产生的原因,规避方法就简单了,即不要在上述的两种情况下做耗时的操作。常见的耗时操作有:网络操作、SD卡文件操作、数据库操作、大量计算等。说起来容易,但做起来有时候会忘,所以Android从2.3开始给我们提供了StrictMode类(android.os.StrictMode),StrictMode提供了两个主要功能:一类是监控UI线程耗时的操作(磁盘读写、网络访问、自定义的运行速度慢的代码等),二是监控内存泄露(如Activity、SQLite、I/O流、大对象未释放等)。如果我们在开发过程中违反了它的监控策略,StrictMode就会在Logcat里面输出警告信息。

如何在App中开启StrictMode呢?一般在MainActiviyt的onCreate函数里面增加如下两行:

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().penaltyDialog().build());// 启用UI线程监控
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());  // 启用虚拟机内存监控

当然,StrictMode适合测试版本,正式发布版本要屏蔽,以免影响App的性能。下面是启用StrictMode后在UI线程写数据库违反策略的一个警告输出例子:

09-04 16:15:34.592: DEBUG/StrictMode(15883): StrictMode policy violation; ~duration=319 ms: android.os.StrictMode$StrictModeDiskWriteViolation: policy=31 violation=1
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk(StrictMode.java:1041)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.database.sqlite.SQLiteStatement.acquireAndLock(SQLiteStatement.java:219)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:83)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.database.sqlite.SQLiteDatabase.updateWithOnConflict(SQLiteDatabase.java:1829)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.database.sqlite.SQLiteDatabase.update(SQLiteDatabase.java:1780)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at com.test.tutorial.data.ListProvider.update(TutListProvider.java:188)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.content.ContentProvider$Transport.update(ContentProvider.java:233)

有调用堆栈和UI线程的耗时时间。

4. 出现ANR时如何分析?

尽管我们进行了预防,但可能有时候有的同学还是没有注意引起了ANR,那么当出现ANR时,我们如何快速定位问题呢?请记住当ANR发生时,android会在两个地方给出重要提示,一个地方是logcat,另一个地方是\data\anr\traces.txt。当然bugreport的信息最全,两者都有。

4.1 Logcat信息

在发生ANR时,Logcat会输出ANR发生的时间、发生ANR前后CPU的使用情况等,如下一个ANR logcat输出信息的例子:

04-01 13:12:11.572 I/InputDispatcher( 220): Application is not responding:Window{2b263310com.android.email/com.android.email.activity.SplitScreenActivitypaused=false}.  5009.8ms since event, 5009.5ms since waitstarted
04-0113:12:11.572 I/WindowManager( 220): Input event dispatching timedout sending tocom.android.email/com.android.email.activity.SplitScreenActivity
04-01 13:12:14.123 I/Process(  220): Sending signal. PID: 21404 SIG: 3---发生ANR的时间和生成trace.txt的时间
……
04-0113:12:15.872 E/ActivityManager(  220): ANR in com.android.email(com.android.email/.activity.SplitScreenActivity)
04-0113:12:15.872 E/ActivityManager(  220): Reason:keyDispatchingTimedOut
04-0113:12:15.872 E/ActivityManager(  220): Load: 8.68 / 8.37 / 8.53
04-0113:12:15.872 E/ActivityManager(  220): CPUusage from 4361ms to 699ms ago ----CPU在ANR发生前的使用情况
04-0113:12:15.872 E/ActivityManager(  220):   5.5%21404/com.android.email: 1.3% user + 4.1% kernel / faults: 10 minor
04-0113:12:15.872 E/ActivityManager(  220):   4.3%220/system_server: 2.7% user + 1.5% kernel / faults: 11 minor 2 major
04-0113:12:15.872 E/ActivityManager(  220):   0.9%52/spi_qsd.0: 0% user + 0.9% kernel
04-0113:12:15.872 E/ActivityManager(  220):   0.5%65/irq/170-cyttsp-: 0% user + 0.5% kernel
04-0113:12:15.872 E/ActivityManager(  220):   0.5%296/com.android.systemui: 0.5% user + 0% kernel
04-0113:12:15.872 E/ActivityManager(  220): 100%TOTAL: 4.8% user + 7.6% kernel + 87% iowait
04-0113:12:15.872 E/ActivityManager(  220): CPUusage from 3697ms to 4223ms later:-- ANR后CPU的使用量
04-0113:12:15.872 E/ActivityManager(  220):   25%21404/com.android.email: 25% user + 0% kernel / faults: 191 minor
04-0113:12:15.872 E/ActivityManager(  220):    16% 21603/__eas(par.hakan: 16% user + 0% kernel
04-0113:12:15.872 E/ActivityManager(  220):    7.2% 21406/GC: 7.2% user + 0% kernel
04-0113:12:15.872 E/ActivityManager(  220):    1.8% 21409/Compiler: 1.8% user + 0% kernel
04-0113:12:15.872 E/ActivityManager(  220):   5.5%220/system_server: 0% user + 5.5% kernel / faults: 1 minor
04-0113:12:15.872 E/ActivityManager(  220):    5.5% 263/InputDispatcher: 0% user + 5.5% kernel
04-0113:12:15.872 E/ActivityManager(  220): 32%TOTAL: 28% user + 3.7% kernel

从LOG可以看出ANR的类型及CPU的使用情况:

  • 如果CPU使用量接近100%,说明当前设备很忙,有可能是CPU饥饿导致了ANR
  • 如果CPU使用量很少,说明主线程被BLOCK了
  • 如果IOwait很高,说明可能是主线程在进行I/O操作造成的

4.2 Traces信息

当ANR发生时,android还会记录ANR信息到/data/anr/traces.txt文件,你可以通过多种方式拿到打开。这个文件只会记录最近发生的一次ANR的堆栈信息及各个线程的运行情况,一般通过堆栈信息我们可以方便定位到阻塞的位置,请看如下traces.txt文件的例子:

-----pid 21404 at 2014-04-01 13:12:14 -----                   ANR发生时间
Cmdline: com.android.email                                             发生ANR的APP包名
DALVIK THREADS:                                                         虚拟机线程信息
(mutexes: tll=0tsl=0 tscl=0 ghl=0 hwl=0 hwll=0)                 
"main" prio=5 tid=1 NATIVE                                                       主线程状态:在执行JNI调用
  | group="main" sCount=1 dsCount=0obj=0x2aad2248 self=0xcf70
  | sysTid=21404 nice=0 sched=0/0cgrp=[fopen-error:2] handle=1876218976
at android.os.MessageQueue.nativePollOnce(Native Method)                               调用堆栈
at android.os.MessageQueue.next(MessageQueue.java:119)
at android.os.Looper.loop(Looper.java:110
)
at android.app.ActivityThread.main(ActivityThread.java:3688)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:624)
at dalvik.system.NativeStart.main(Native Method)
…其他线程

Thread状态说明:
ThreadState (defined at “dalvik/vm/thread.h “)
THREAD_UNDEFINED = -1, /* makes enum compatible with int32_t */
THREAD_ZOMBIE = 0, /* TERMINATED */
THREAD_RUNNING = 1, /* RUNNABLE or running now */
THREAD_TIMED_WAIT = 2, /* TIMED_WAITING in Object.wait() */
THREAD_MONITOR = 3, /* BLOCKED on a monitor */
THREAD_WAIT = 4, /* WAITING in Object.wait() */
THREAD_INITIALIZING= 5, /* allocated, not yet running */
THREAD_STARTING = 6, /* started, not yet on thread list */
THREAD_NATIVE = 7, /* off in a JNI native method */
THREAD_VMWAIT = 8, /* waiting on a VM resource */
THREAD_SUSPENDED = 9, /* suspended, usually by GC or debugger */

5. ANR案例

5.1 UI线程block导致ANR

MainHandler post runnable执行加载所有已安装app的信息在某些手机上ANR,原因是MainHandler post runnable运行在UI线程,而加载所有已安装app的信息会设计到大量解析计算,造成了UI回调超时。

----- pid 2848 at 2014-07-22 17:01:29 -----
Cmd line: com.tencent.qfilemanager

DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
"main" prio=5 tid=1 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x40cc3538 self=0xabdf30
  | sysTid=2848 nice=0 sched=0/0 cgrp=default handle=1074267496
  | schedstat=( 0 0 0 ) utm=64 stm=58 core=1
  at android.content.res.AssetManager.getStringBlockCount(Native Method)
  at android.content.res.AssetManager.makeStringBlocks(AssetManager.java:257)
  at android.content.res.AssetManager.ensureStringBlocks(AssetManager.java:249)
  at android.content.res.Resources.<init>(Resources.java:206)
  at android.app.ActivityThread.getTopLevelResources(ActivityThread.java:1492)
  at android.app.ActivityThread.getTopLevelResources(ActivityThread.java:1519)
  at android.app.ApplicationPackageManager.getResourcesForApplication(ApplicationPackageManager.java:689)
  at android.app.ApplicationPackageManager.getText(ApplicationPackageManager.java:891)
  at android.content.pm.PackageItemInfo.loadLabel(PackageItemInfo.java:115)
  at com.tencent.qfilemanager.fileanalysis.PkgInfo.reloadPkgInfo(PkgInfo.java:50)
  at com.tencent.qfilemanager.fileanalysis.PkgCache.loadAllInstalledPackages(PkgCache.java:29)
  at com.tencent.qfilemanager.activity.FileManagerActivity$3.run(FileManagerActivity.java:209)
  at android.os.Handler.handleCallback(Handler.java:605)
  at android.os.Handler.dispatchMessage(Handler.java:92)
  at android.os.Looper.loop(Looper.java:137)
  at android.app.ActivityThread.main(ActivityThread.java:4456)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:511)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:787)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:554)
  at dalvik.system.NativeStart.main(Native Method)

5.2 UI线程高 iowait导致ANR

在AsyncTask的onPostExecute里面批量数据库操作,导致ANR。原因,AsyncTask的onPostExecute执行在UI线程,批量数据库操作是I/O操作,速度较慢,UI线程回调超时。

Process:com.android.email
Activity:com.android.email/.activity.MessageView
Subject:keyDispatchingTimedOut
CPU usage from 2550ms to -2814ms ago:
5%187/system_server: 3.5% user + 1.4% kernel / faults: 86 minor 20major
4.4% 1134/com.android.email: 0.7% user + 3.7% kernel /faults: 38 minor 19 major
4% 372/com.android.eventstream: 0.7%user + 3.3% kernel / faults: 6 minor
1.1% 272/com.android.phone:0.9% user + 0.1% kernel / faults: 33 minor
0.9%252/com.android.systemui: 0.9% user + 0% kernel
0%409/com.android.eventstream.telephonyplugin: 0% user + 0% kernel /faults: 2 minor
0.1% 632/com.android.devicemonitor: 0.1% user + 0%kernel
100%TOTAL: 6.9% user + 8.2% kernel +84%iowait
-----pid 1134 at 2010-12-17 17:46:51 -----
Cmd line:com.android.email
DALVIK THREADS:
(mutexes: tll=0 tsl=0tscl=0 ghl=0 hwl=0 hwll=0)
"main" prio=5 tid=1 WAIT
|group="main" sCount=1 dsCount=0 obj=0x2aaca180self=0xcf20
| sysTid=1134 nice=0 sched=0/0 cgrp=[fopen-error:2]handle=1876218976
at java.lang.Object.wait(Native Method)
-waiting on <0x2aaca218> (a java.lang.VMThread)
atjava.lang.Thread.parkFor(Thread.java:1424)

atandroid.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1271)
atcom.android.email.provider.EmailProvider.query(EmailProvider.java:1098)
atandroid.content.ContentProvider$Transport.query(ContentProvider.java:187)
atandroid.content.ContentResolver.query(ContentResolver.java:268)
atcom.android.email.provider.EmailContent$Message.restoreMessageWithId(EmailContent.java:648)
atcom.android.email.Controller.setMessageRead(Controller.java:658)
atcom.android.email.activity.MessageView.onMarkAsRead(MessageView.java:700)
atcom.android.email.activity.MessageView.access$2500(MessageView.java:98)
atcom.android.email.activity.MessageView$LoadBodyTask.onPostExecute(MessageView.java:1290)
atcom.android.email.activity.MessageView$LoadBodyTask.onPostExecute(MessageView.java:1255)
atandroid.os.AsyncTask.finish(AsyncTask.java:417)
atandroid.os.AsyncTask.access$300(AsyncTask.java:127)
atandroid.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
atandroid.os.Handler.dispatchMessage(Handler.java:99)
atandroid.os.Looper.loop(Looper.java:123)
atandroid.app.ActivityThread.main(ActivityThread.java:3652)
atjava.lang.reflect.Method.invokeNative(Native Method)
atjava.lang.reflect.Method.invoke(Method.java:507)
atcom.android.internal.os.ZygoteIn

理解ANR的前世今生,解决ANR就是这么简单!

时间: 07-22

理解ANR的前世今生,解决ANR就是这么简单!的相关文章

【Android】定位与解决anr错误记录

问题描述 cocos2d-x游戏项目android工程接入sdk,支付成功后,java代码回调lua方法,产生了anr. 如何定位anr? 在data/anr/traces.txt文件中记录和anr错误信息.可以使用RE管理器查看该文件. 在日志信息中可以看到回调方法中调用的cocos2d-x的音频引擎播放音效的方法.就在这里产生了anr.我这里使用的是cocos2d-x2.1.5. 找到出问题的根源后,我把播放音效的代码注释了.然后再运行,就没有产生anr了. 但是,另一个问题又出现了...

Android ANR原因及解决办法

1.什么是ANR 在Android中,应用程序的响应性是由ActivityManager和WindowManager系统服务监视的.当监测到以下情况中的一个时,Android就会针对特定的应用程序显示ANR A activity中5秒内没有响应输入事件(如按键按下.屏幕触摸) B BroadcastReceiver在10秒内没有执行完毕 造成以上情况出现的原因有很多,主要的原因是在主线程中做了非常耗时的操作,如下载.IO异常等. 2.如何避免ANR 一般耗时的操作,如网络及数据库的访问.高耗时计

jpa缓存导致无法查询到更新后的数据&amp;android出现ANR的一个解决办法

1. 向服务器更新记录后查询,始终查询不到更新后的信息 只能查到更新之前的,马上推断出是缓存的问题.网上搜索一番,将问题定位为jpa缓存,我们要设置jpa查询时不从缓存中取,直接从数据库中取,这样便能保证查询到的结果是最新的,但是性能可能会有所影响.参考资料:https://en.wikibooks.org/wiki/Java_Persistence/Caching#JPA_2.0_Cache_APIs 如上图提示,我在restful服务器做了如下设置后便可以了. 2. 安卓app注册界面出新了

ANR 是什么?怎样避免和解决ANR

在Android 上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框.用户可以选择让程序继续运行,但是,他们在使用你的应用程序时,并不希望每次都要处理这个对话框.因此,在程序里对响应性能的设计很重要,这样,系统不会显示ANR 给用户.Activity 5 秒broadcast10 秒耗时的操作worker thread 里面完成, handler message…AsynTas

Android ANR异常及解决方法

今天面试遇到这道题,刚开始没看出这个anr缩写的意思,后来问了面试官原来是这个意思: ANRs ("Application Not Responding"),意思是"应用没有响应". 在如下情况下,Android会报出ANR错误: – 主线程 ("事件处理线程" / "UI线程") 在5秒内没有响应输入事件 – BroadcastReceiver 没有在10秒内完成返回 通常情况下,下面这些做法会导致ANR 1.在主线程内进行

通过 GOOGLE 大数据计算平台演进理解 APACHE FLINK 前世今生

一.背景 2019年1月,伴随 APACHE FLINK 母公司 Data Artisans 被 收购 ,FLINK 毫无争议成为继 SPARK 之后的新一代大数据计算平台,本文希望通过 GOOGLE 计算平台演进来更好的理解 FLINK. 二.GOOGLE 大数据计算平台演进 GOOGLE 作为搜索引擎的顶级公司,需要处理海量数据,其大数据计算平台的演进是行业的风向标:本文通过 GOOGLE 在该领域发表的论文进行剖析,希望从中提取一些演进的主线. 2.1 分布式的三篇经典 2003年,[Th

python网络爬虫实战-Scrapy,深入理解scrapy框架,解决数据抓取过程

一 前言 二 网络爬虫简介 审查元素 简单实例 1 requests安装 2 简单实例 三 爬虫实战 小说下载 1 实战背景 2 小试牛刀 3Beautiful Soup 3整合代码 优美壁纸下载 1实战背景 2实战进阶 3整合代码 爱奇艺VIP视频下载 1实战背景 2实战升级 3编写代码 四 总结 一 前言 强烈建议:请在电脑的陪同下,阅读本文.本文以实战为主,阅读过程如稍有不适,还望多加练习. 本文的实战内容有: 网络小说下载(静态网站) 优美壁纸下载(动态网站) 爱奇艺VIP视频下载 二

理解计算机的工作方式——通过汇编一个简单的C程序并分析汇编代码

Author: 翁超平 Notice:原创作品转载请注明出处 See also:<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000  本文通过汇编一个简单的C程序,并分析汇编代码,来理解计算机是如何工作的.整个过程都在实验楼上完成,感兴趣的读者可以通过上面给出的课程链接自行动手学习.以下是实验过程和结果. 一.操作步骤 1.首先在通过vim程序建立main.c文件.代码如下: 图1 2.使用如下命令将main.c编

QQ互联 redirect uri is illegal(100010)的解决办法,很简单

我的地址栏内容是:http://openapi.qzone.qq.com/oauth/show?which=ConfirmPage&display=pc&response_type=code&client_id=101227039&state=4381ac9e3d224478a2f194d69bc81589&redirect_uri=http%3a%2f%2fdt.hztc.com.cn%2fapi%2foauth%2fqq%2freturn_url.aspx&am