当前位置:   article > 正文

Android App开发之ANR异常的原因分析及处理总结_app的anr

app的anr

Android App开发之ANR异常的原因分析及处理总结

ANR的全称是application not responding,根据它的意思我们就能看出来是应用程序未响应,就像是我们在电脑上碰到的程序未响应,一般电脑出现这种情况,可能是由于我们打开了很多应用程序,占用了大量的内存,或者CPU时间片被一个应用程序长时间占用,不够分配,导致部分应用程序出现了无响应。Android和Windows基本一样,接下来我们就分析一下Android是怎么产生的这个问题。

下图就是我们见到的APP无响应的时候出现的对话框,产生ANR的原因很多,但是只有在Activty中的Anr才会弹出对话框,对话框有两个选项,一个是等待,一个是关闭程序供用户选择:
ANR图片

一、为什么会产生ANR(Application not response)这种情况呢?
在Android里, App的响应能力是由Activity Manager和Window Manager系统服务来监控的.,通常情况下产生ANR有这么三个条件:

1.只有主线程才会产生ANR,主线程就是UI线程;

2.必须发生某些输入事件或特定操作,比如按键或触屏等输入事件,在BroadcastReceiver或Service的各个生命周期调用函数;

3.上述事件响应超时,不同的context规定的上限时间不同

具备了以上三个条件,那么加上这两个环境,就会产生ANR了:

1.5秒内无法对输入事件(按键及触摸)做出响应

2.广播接收器无法在10秒内结束运行

二、ANR时系统做了什么
1.弹出一个对话框

2.将ANR信息输出到traces.txt文件中

traces.txt文件是一个ANR记录文件,用于开发人员调试,目录位于/data/anr中,无需root权限即可通过pull命令获取,下面的命令可以将traces.txt文件拷贝到当前目录下

adb pull /data/anr
3.将ANR信息输出到Logcat中

三、对ANR产生详细分析
为什么会产生ANR,在第一条讲解中,具体剖析(部分文章参考简书作者):

a.主线程对输入事件5秒内没有处理完毕

b.主线程在执行BroadcastReceiver的onReceive()函数时10秒内没有处理完毕

c.主线程在Service的各个生命周期函数时20秒内没有处理完毕。

那么导致ANR的根本原因是什么呢?简单的总结有以下两点:

1.主线程执行了耗时操作,比如数据库操作或网络编程

2.其他进程(就是其他程序)占用CPU导致本进程得不到CPU时间片,比如其他进程的频繁读写操作可能会导致这个问题。

细分的话,导致ANR的原因有如下几点:

1.耗时的网络访问

2.大量的数据读写

3.数据库操作

4.硬件操作(比如camera)

5.调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候

6.service binder的数量达到上限

7.system server中发生WatchDog ANR

8.service忙导致超时无响应

9.其他线程持有锁,导致主线程等待超时

10.其它线程终止或崩溃导致主线程一直等待。

四、ANR机制的实现原理:

文章:http://gityuan.com/2016/07/02/android-anr/从源码角度详细的分析了ANR机制实现的原理。对于上一章讲到的1-4中情况,分别找到了其源码中是如何实现的,对于每一种大概原理如下:1.在进行相关操作调用hander.sendMessageAtTime()发送一个ANR的消息,延时时间为ANR发生的时间(如前台Service是当前时间20s之后)。2.进行相关的操作3.操作结束后向remove掉该条message。如果相关的操作在规定时间没有执行完成,该条message将被handler取出并执行,就发生了ANR。

下面以BroadcastReceiver为例详细介绍:

BroadcastQueue.processNextBroadcast()

final void processNextBroadcast(boolean fromMsg) { … synchronized (mService) { … do { if (mOrderedBroadcasts.size() == 0) { … if (mService.mProcessesReady && r.dispatchTime > 0) { long now = SystemClock.uptimeMillis(); if ((numReceivers > 0) && (now > r.dispatchTime + (2 * mTimeoutPeriod * numReceivers))) { //1.发送延时消息 broadcastTimeoutLocked(false); // forcibly finish this broadcast forceReceive = true; r.state = BroadcastRecord.IDLE; } } if (r.state != BroadcastRecord.IDLE) { if (DEBUG_BROADCAST) Slog.d(TAG, “processNextBroadcast(” + mQueueName + “) called when not idle (state=” + r.state + “)”); return; } if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) { // No more receivers for this broadcast! Send the final // result if requested… if (r.resultTo != null) { try { //2. 处理广播消息 performReceiveLocked(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, false, false, r.userId); // Set this to null so that the reference // (local and remote) isn’t kept in the mBroadcastHistory. r.resultTo = null; } catch (RemoteException e) { … } } //3.取消延时消息 cancelBroadcastTimeoutLocked(); … } } while (r == null) ; … } } }
1.发送延时消息:broadcastTimeoutLocked(false): final void broadcastTimeoutLocked(boolean fromMsg) { … long now = SystemClock.uptimeMillis(); if (fromMsg) { if (mService.mDidDexOpt) { // Delay timeouts until dexopt finishes. mService.mDidDexOpt = false; long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod; setBroadcastTimeoutLocked(timeoutTime); return; } if (!mService.mProcessesReady) { return; } long timeoutTime = r.receiverTime + mTimeoutPeriod; if (timeoutTime > now) { setBroadcastTimeoutLocked(timeoutTime); return; } }
他调用了setBroadcastTimeoutLocked(long timeoutTime): final void setBroadcastTimeoutLocked(long timeoutTime) { if (! mPendingBroadcastTimeoutMessage) { Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this); mHandler.sendMessageAtTime(msg, timeoutTime); mPendingBroadcastTimeoutMessage = true; } }
传入setBroadcastTimeoutLocked(long timeoutTime)的时间xxx + mTimeoutPeriod,mTimeoutPeriod就是onRecieve()可以执行的时间,在BroadcastQueue初始化时候被赋值,前台队列为10s后台队列为60s:
ActivityManagerService.java: public ActivityManagerService(Context systemContext) { … static final int BROADCAST_FG_TIMEOUT = 10 * 1000; static final int BROADCAST_BG_TIMEOUT = 60 * 1000; … mFgBroadcastQueue = new BroadcastQueue(this, mHandler, “foreground”, BROADCAST_FG_TIMEOUT, false); mBgBroadcastQueue = new BroadcastQueue(this, mHandler, “background”, BROADCAST_BG_TIMEOUT, true); … }
performReceiveLocked()为广播的实际处理,就不展开了

cancelBroadcastTimeoutLocked() :

该方法的主要工作是当service启动完成,则移除服务超时消息SERVICE_TIMEOUT_MSG。

final void cancelBroadcastTimeoutLocked() {
if (mPendingBroadcastTimeoutMessage) {
mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
mPendingBroadcastTimeoutMessage = false;
}

五、如何避免ANR呢?
1.避免在主线程执行耗时操作,所有耗时操作应新开一个子线程完成,然后再在主线程更新UI。

2.BroadcastReceiver要执行耗时操作时应启动一个service,将耗时操作交给service来完成。

3.避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广 播时需要向用户展示什么,你应该使用Notification Manager来实现。

UI线程尽量只做跟UI相关的工作

耗时的工作()比如数据库操作,I/O,网络操作),采用单独的工作线程处理

用Handler来处理UIthread和工作thread的交互

UI线程,例如:

Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick(),etc

AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel,etc

Mainthread handler: handleMessage(), post*(runnable r), etc

ANR分析:需要关注CPU/IO,trace死锁等数据。

六、总结
ANR在我们开发APP中也比较常见,总的来说,我们需要执行耗时得操作就在子线程中执行,避免在主线程中执行。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号