作者:Marker_Sky
一、ANR说明和原因
1.1 简介
ANR全称:Application Not Responding,也就是应用程序无响应。
1.2 原因
Android系统中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现ANR。
以下四个条件都可以造成ANR发生:
- InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件
- BroadcastQueue Timeout:在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。
- Service Timeout:前台服务20秒内,后台服务在200秒内没有执行完毕。
- ContentProvider Timeout:ContentProvider的publish在10s内没进行完。
1.3 避免
尽量避免在主线程(UI线程)中作耗时操作。
那么耗时操作就放在子线程中。关于多线程可以参考:Android多线程:理解和简单使用总结
二、ANR分析办法
2.1 ANR重现
这里使用的是号称Google亲儿子的Google Pixel xl(Android 8.0系统)做的测试,生成一个按钮跳转到ANRTestActivity,在后者的onCreate()中主线程休眠20秒:
@OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_anr_test);// 这是Android提供线程休眠函数,与Thread.sleep()最大的区别是// 该使用该函数不会抛出InterruptedException异常。SystemClock.sleep(20*1000);}
在进入ANRTestActivity后黑屏一段时间,大概有七八秒,终于弹出了ANR异常。
2.2 ANR分析办法一:Log
刚才产生ANR后,看下Log:
可以看到logcat清晰地记录了ANR发生的时间,以及线程的tid和一句话概括原因:WaitingInMainSignalCatcherLoop,大概意思为主线程等待异常。最后一句The application may be doing too much work on its main thread.告知可能在主线程做了太多的工作。
2.3 ANR分析办法二:traces.txt
刚才的log有第二句Wrote stack traces to /data/anr/traces.txt,说明ANR异常已经输出到traces.txt文件,使用adb命令把这个文件从手机里导出来:
- cd到adb.exe所在的目录,也就是Android SDK的platform-tools目录,例如:
cdD:\Android\AndroidSdk\platform-tools
此外,除了Windows的cmd以外,还可以使用AndroidStudio的Terminal来输入adb命令。
2.到指定目录后执行以下adb命令导出traces.txt文件:
adbpull/data/anr/traces.txt
traces.txt默认会被导出到Android SDK的\platform-tools目录。一般来说traces.txt文件记录的东西会比较多,分析的时候需要有针对性地去找相关记录。
-----pid23346at2017-11-0711:33:57--------->进程id和ANR产生时间Cmdline:com.sky.myjavatestBuildfingerprint:google/marlin/marlin:8.0.0/OPR3.170623.007/4286350:user/release-keysABI:arm64Buildtype:optimizedZygoteloadedclasses=4681postzygoteclasses=106Interntable:42675strong;137weakJNI:CheckJNIison;globals=526(plus22weak)Libraries:/system/lib64/libandroid.so/system/lib64/libcompiler_rt.so/system/lib64/libjavacrypto.so/system/lib64/libjnigraphics.so/system/lib64/libmedia_jni.so/system/lib64/libsoundpool.so/system/lib64/libwebviewchromium_loader.solibjavacore.solibopenjdk.so(9)Heap:22%free,1478KB/1896KB;21881objects---->内存使用情况..."main"prio=5tid=1Sleeping---->原因为Sleeping|group="main"sCount=1dsCount=0flags=1obj=0x733d0670self=0x74a4abea00|sysTid=23346nice=-10cgrp=defaultsched=0/0handle=0x74a91ab9b0|state=Sschedstat=(39146212882838177354)utm=33stm=4core=3HZ=100|stack=0x7fe6fac000-0x7fe6fae000stackSize=8MB|heldmutexes=atjava.lang.Thread.sleep(Nativemethod)-sleepingon<0x053fd2c2>(ajava.lang.Object)atjava.lang.Thread.sleep(Thread.java:373)-locked<0x053fd2c2>(ajava.lang.Object)atjava.lang.Thread.sleep(Thread.java:314)atandroid.os.SystemClock.sleep(SystemClock.java:122)atcom.sky.myjavatest.ANRTestActivity.onCreate(ANRTestActivity.java:20)---->产生ANR的包名以及具体行数atandroid.app.Activity.performCreate(Activity.java:6975)atandroid.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)atandroid.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)atandroid.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)atandroid.app.ActivityThread.-wrap11(ActivityThread.java:-1)atandroid.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)atandroid.os.Handler.dispatchMessage(Handler.java:105)atandroid.os.Looper.loop(Looper.java:164)atandroid.app.ActivityThread.main(ActivityThread.java:6541)atjava.lang.reflect.Method.invoke(Nativemethod)atcom.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)atcom.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
在文件中使用 ctrl + F 查找包名可以快速定位相关代码。通过上方log可以看出相关问题:
- 进程id和包名:pid 23346 com.sky.myjavatest
- 造成ANR的原因:Sleeping
- 造成ANR的具体行数:ANRTestActivity.java:20类的第20行
特别注意:产生新的ANR,原来的 traces.txt 文件会被覆盖。
2.4 ANR分析办法三:Java线程调用分析
通过JDK提供的命令可以帮助分析和调试Java应用,命令为:
jstack{pid}
其中pid可以通过jps命令获得,jps命令会列出当前系统中运行的所有Java虚拟机进程,比如
7266Test7267Jps
具体分析参考:Android应用ANR分析 四.1节
2.5 ANR分析办法四:DDMS分析ANR问题
- 使用DDMS——Update Threads工具
- 阅读Update Threads的输出
具体分析参考:Android应用ANR分析 四.2节
三、造成ANR的原因及解决办法
上面例子只是由于简单的主线程耗时操作造成的ANR,造成ANR的原因还有很多:
- 主线程阻塞或主线程数据读取
解决办法:避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。尽量避免在主线程query provider、不要滥用SharePreferenceS
- CPU满负荷,I/O阻塞
解决办法:文件读写或数据库操作放在子线程异步操作。
- 内存不足
解决办法:AndroidManifest.xml文件中可以设置 android:largeHeap=”true”,以此增大App使用内存。不过不建议使用此法,从根本上防止内存泄漏,优化内存使用才是正道。
- 各大组件ANR
各大组件生命周期中也应避免耗时操作,注意BroadcastReciever的onRecieve()、后台Service和ContentProvider也不要执行太长时间的任务。
四、ANR源码分析
特别声明:文章 理解Android ANR的触发原理 分别记录了由Service、BroadcastReceiver和ContentProvider造成的ANR。下文引用该文代码,并依据自己的简单理解作总结。
4.1 Service造成的Service Timeout
Service Timeout是位于“ActivityManager”线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时触发。
4.1.1 发送延时消息
Service进程attach到system_server进程的过程中会调用realStartServiceLocked,紧接着mAm.mHandler.sendMessageAtTime()来发送一个延时消息,延时的时常是定义好的,如前台Service的20秒。ActivityManager线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时会触发。
AS.realStartServiceLocked
ActiveServices.java
privatefinalvoidrealStartServiceLocked(ServiceRecordr,ProcessRecordapp,booleanexecInFg)throwsRemoteException{...//发送delay消息(SERVICE_TIMEOUT_MSG)bumpServiceExecutingLocked(r,execInFg,"create");try{...//最终执行服务的onCreate()方法app.thread.scheduleCreateService(r,r.serviceInfo,mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),app.repProcState);}catch(DeadObjectExceptione){mAm.appDiedLocked(app);throwe;}finally{...}}
AS.bumpServiceExecutingLocked
privatefinalvoidbumpServiceExecutingLocked(ServiceRecordr,booleanfg,Stringwhy){...scheduleServiceTimeoutLocked(r.app);}voidscheduleServiceTimeoutLocked(ProcessRecordproc){if(proc.executingServices.size()==0||proc.thread==null){return;}longnow=SystemClock.uptimeMillis();Messagemsg=mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_TIMEOUT_MSG);msg.obj=proc;//当超时后仍没有remove该SERVICE_TIMEOUT_MSG消息,则执行service Timeout流程mAm.mHandler.sendMessageAtTime(msg,proc.execServicesFg?(now+SERVICE_TIMEOUT):(now+SERVICE_BACKGROUND_TIMEOUT));}
4.1.2 进入目标进程的主线程创建Service
经过Binder等层层调用进入目标进程的主线程 handleCreateService(CreateServiceData data)。
ActivityThread.java
privatevoidhandleCreateService(CreateServiceDatadata){...java.lang.ClassLoadercl=packageInfo.getClassLoader();Serviceservice=(Service)cl.loadClass(data.info.name).newInstance();...try{//创建ContextImpl对象ContextImplcontext=ContextImpl.createAppContext(this,packageInfo);context.setOuterContext(service);//创建Application对象Applicationapp=packageInfo.makeApplication(false,mInstrumentation);service.attach(context,this,data.info.name,data.token,app,ActivityManagerNative.getDefault());//调用服务onCreate()方法service.onCreate();//取消AMS.MainHandler的延时消息ActivityManagerNative.getDefault().serviceDoneExecuting(data.token,SERVICE_DONE_EXECUTING_ANON,0,0);}catch(Exceptione){...}}
这个方法中会创建目标服务对象,以及回调常用的Service的onCreate()方法,紧接着通过serviceDoneExecuting()回到system_server执行取消AMS.MainHandler的延时消息。
4.1.3 回到system_server执行取消AMS.MainHandler的延时消息
AS.serviceDoneExecutingLocked
privatevoidserviceDoneExecutingLocked(ServiceRecordr,booleaninDestroying,booleanfinishing){...if(r.executeNesting<=0){if(r.app!=null){r.app.execServicesFg=false;r.app.executingServices.remove(r);if(r.app.executingServices.size()==0){//当前服务所在进程中没有正在执行的servicemAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG,r.app);...}...}
此方法中Service逻辑处理完成则移除之前延时的消息SERVICE_TIMEOUT_MSG。如果没有执行完毕不调用这个方法,则超时后会发出SERVICE_TIMEOUT_MSG来告知ANR发生。
4.2BroadcastReceiver造成的BroadcastQueue Timeout
BroadcastReceiver Timeout是位于”ActivityManager”线程中的BroadcastQueue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息时触发。
4.2.1 处理广播函数 processNextBroadcast() 中 broadcastTimeoutLocked(false) 发送延时消息
广播处理顺序为先处理并行广播,再处理当前有序广播。
finalvoidprocessNextBroadcast(booleanfromMsg){synchronized(mService){...// 处理当前有序广播do{r=mOrderedBroadcasts.get(0);//获取所有该广播所有的接收者intnumReceivers=(r.receivers!=null)?r.receivers.size():0;if(mService.mProcessesReady&&r.dispatchTime>0){longnow=SystemClock.uptimeMillis();if((numReceivers>0)&&(now>r.dispatchTime+(2*mTimeoutPeriod*numReceivers))){//step 1\. 发送延时消息,这个函数处理了很多事情,比如广播处理超时结束广播broadcastTimeoutLocked(false);...}}if(r.receivers==null||r.nextReceiver>=numReceivers||r.resultAbort||forceReceive){if(r.resultTo!=null){//2\. 处理广播消息消息performReceiveLocked(r.callerApp,r.resultTo,newIntent(r.intent),r.resultCode,r.resultData,r.resultExtras,false,false,r.userId);r.resultTo=null;}//3\. 取消广播超时ANR消息cancelBroadcastTimeoutLocked();}}while(r==null);...// 获取下条有序广播r.receiverTime=SystemClock.uptimeMillis();if(!mPendingBroadcastTimeoutMessage){longtimeoutTime=r.receiverTime+mTimeoutPeriod;//设置广播超时setBroadcastTimeoutLocked(timeoutTime);}...}}
上文的step 1. broadcastTimeoutLocked(false)函数:记录时间信息并调用函数设置发送延时消息
finalvoidbroadcastTimeoutLocked(booleanfromMsg){...longnow=SystemClock.uptimeMillis();if(fromMsg){if(mService.mDidDexOpt){// Delay timeouts until dexopt finishes.mService.mDidDexOpt=false;longtimeoutTime=SystemClock.uptimeMillis()+mTimeoutPeriod;setBroadcastTimeoutLocked(timeoutTime);return;}if(!mService.mProcessesReady){return;}longtimeoutTime=r.receiverTime+mTimeoutPeriod;if(timeoutTime>now){// step 2setBroadcastTimeoutLocked(timeoutTime);return;}}
上文的step 2.setBroadcastTimeoutLocked函数: 设置广播超时具体操作,同样是发送延时消息
finalvoidsetBroadcastTimeoutLocked(longtimeoutTime){if(!mPendingBroadcastTimeoutMessage){Messagemsg=mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG,this);mHandler.sendMessageAtTime(msg,timeoutTime);mPendingBroadcastTimeoutMessage=true;}}
4.2.2 setBroadcastTimeoutLocked(long timeoutTime)函数的参数timeoutTime是当前时间加上设定好的超时时间。
也就是上文的
longtimeoutTime=SystemClock.uptimeMillis()+mTimeoutPeriod;
mTimeoutPeriod 也就是前台队列的10s和后台队列的60s。
publicActivityManagerService(ContextsystemContext){...staticfinalintBROADCAST_FG_TIMEOUT=10*1000;staticfinalintBROADCAST_BG_TIMEOUT=60*1000;...mFgBroadcastQueue=newBroadcastQueue(this,mHandler,"foreground",BROADCAST_FG_TIMEOUT,false);mBgBroadcastQueue=newBroadcastQueue(this,mHandler,"background",BROADCAST_BG_TIMEOUT,true);...}
4.2.3 在processNextBroadcast()过程,执行完performReceiveLocked后调用cancelBroadcastTimeoutLocked
cancelBroadcastTimeoutLocked :处理广播消息函数 processNextBroadcast() 中 performReceiveLocked() 处理广播消息完毕则调用 cancelBroadcastTimeoutLocked() 取消超时消息。
finalvoidcancelBroadcastTimeoutLocked(){if(mPendingBroadcastTimeoutMessage){mHandler.removeMessages(BROADCAST_TIMEOUT_MSG,this);mPendingBroadcastTimeoutMessage=false;}}
4.3 ContentProvider的ContentProvider Timeout
ContentProvider Timeout是位于ActivityManager线程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息时触发。
五、Android ANR的信息收集
无论是四大组件或者进程等只要发生ANR,最终都会调用AMS.appNotResponding()方法。
声明:本文部分素材转载自互联网,如有侵权立即删除 。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别
丞旭猿论坛
暂无评论内容