android如何在日志中判断anr,Android ANR问题专题

android ANR发生的原因总结和解决办法

ANR的全称是application not responding,意思就是程序未响应,类似于我们在windows上见到的程序未响应。ANR发生会使用户觉得我们的程序不友好,那么什么情况会导致ANR的发生呢?

首先ANR的发生是有条件限制的,分为以下三点:

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

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

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

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

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

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

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

1.耗时的网络访问

2.大量的数据读写

3.数据库操作

4.硬件操作(比如camera)

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

6.service binder的数量达到上限

7.system server中发生WatchDogANR

8.service忙导致超时无响应

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

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

那么如何避免ANR的发生呢或者说ANR的解决办法是什么呢?

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

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

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

1.3 如何解决ANR

使用AsyncTask处理耗时IO操作

使用Thread(不可以创建handler)或者HandlerThread提高优先级

使用Handler来处理工作线程的耗时任务

Activity的onCreate和onResume回调中尽量少写耗时操作的代码

1.4 ANR问题排查

1 获取trace.txt文件

adb shell cat /data/anr/traces.txt > d:/traces.txt (拷贝到d盘)

2 根据trace.txt分析anr问题的原因

从LOG可以看出ANR的类型,CPU的使用情况,如果CPU使用量接近100%,说明当前设备很忙,有可能是CPU饥饿导致了ANR

如果CPU使用量很少,说明主线程被BLOCK了

如果IOwait很高,说明ANR有可能是主线程在进行I/O操作造成的

除了看LOG,解决ANR还得需要trace.txt文件,

ANR的监测机制:首先分析Service和输入事件大致工作流程,然后从Service,InputEvent两种不同的ANR监测机制的源码实现开始,分析了Android如何发现各类ANR。在启动服务、输入事件分发时,植入超时检测,用于发现ANR。

ANR的报告机制:分析Android如何输出ANR日志。当ANR被发现后,两个很重要的日志输出是:CPU使用情况和进程的函数调用栈,这两类日志是我们解决ANR问题的利器。

监测ANR的核心原理是消息调度和超时处理。

只有被ANR监测的场景才会有ANR报告以及ANR提示框。

我们先抛出两个问题问题一:Service启动流程?问题一: 如何监测Service超时?

1. Service启动流程如下图所示:

51a00c232c32

(1)ActiveServices.realStartServiceLocked()在通过app.thread的scheduleCreateService()来创建Service对象并调用Service.onCreate()后,接着又调用sendServiceArgsLocked()方法来调用Service的其他方法,如onStartCommand。以上两步均是进程间通信,应用与AMS之间跨进程通信可以参考应用进程与系统进程通信(2)以上只是列出Service启动流程的关键步骤,具体每个方法主要做哪些工作还需要查看具体的代码,暂时先忽略这些,感兴趣的可以参考Android开发艺术探索等其他相关资料

2. Service超时监测机制Service超时监测机制可以从Service启动流程中找到。

(1)ActiveServices.realStartServiceLocked()主要工作有

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { ... // 主要是为了设置ANR超时,可以看出在正式启动Service之前开始ANR监测; bumpServiceExecutingLocked(r, execInFg, "create"); // 启动过程调用scheduleCreateService方法,最终会调用Service.onCreate方法; app.thread.scheduleCreateService(r, r.serviceInfo, // 绑定过程中,这个方法中会调用app.thread.scheduleBindService方法 requestServiceBindingsLocked(r, execInFg); // 调动Service的其他方法,如onStartCommand,也是IPC通讯 sendServiceArgsLocked(r, execInFg, true); }

(2)bumpServiceExecutingLocked()会调用scheduleServiceTimeoutLocked()方法

void scheduleServiceTimeoutLocked(ProcessRecord proc) { if (proc.executingServices.size() == 0 || proc.thread == null) { return; } Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_TIMEOUT_MSG); msg.obj = proc; // 在serviceDoneExecutingLocked中会remove该SERVICE_TIMEOUT_MSG消息, // 当超时后仍没有remove SERVICE_TIMEOUT_MSG消息,则执行ActiveServices. serviceTimeout()方法; mAm.mHandler.sendMessageDelayed(msg, proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT); // 前台进程中执行Service,SERVICE_TIMEOUT=20s;后台进程中执行Service,SERVICE_BACKGROUND_TIMEOUT=200s }

(3)如果在指定的时间内还没有serviceDoneExecutingLocked()方法将消息remove掉,就会调用ActiveServices. serviceTimeout()方法

void serviceTimeout(ProcessRecord proc) { ... final long maxTime = now - (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT); ... // 寻找运行超时的Service for (int i=proc.executingServices.size()-1; i>=0; i--) { ServiceRecord sr = proc.executingServices.valueAt(i); if (sr.executingStart < maxTime) { timeout = sr; break; } ... } ... // 判断执行Service超时的进程是否在最近运行进程列表,如果不在,则忽略这个ANR if (timeout != null && mAm.mLruProcesses.contains(proc)) { anrMessage = "executing service " + timeout.shortName; } ... if (anrMessage != null) { // 当存在timeout的service,则执行appNotResponding,报告ANR mAm.appNotResponding(proc, null, null, false, anrMessage); }}

(4)Service onCreate超时监测整体流程如下图

51a00c232c32

输入事件超时监测

记一次分析解决ANR过程

经过我查看log信息发现

Reason: Input dispatching timed out (Waiting because the touched window has not finished processing the input events that were previously delivered to it.)

Load: 0.9 / 0.57 / 0.68

CPU usage from 2505ms to -3307ms ago:

==94%== 20357/com.richeninfo.cmoa: 94% user+ 0.3% kernel / faults: 1 minor

22% 810/system_server: 17% user + 5.1% kernel / faults: 1061 minor

0.5% 146/debuggerd: 0.2% user + 0.3% kernel / faults: 2717 minor

3.4% 977/com.android.systemui: 3.2% user + 0.1% kernel / faults: 11 minor

1.8% 1310/com.android.phone: 1.5% user + 0.3% kernel

从LOG可以看出ANR的类型,CPU的使用情况,如果CPU使用量接近100%,说明当前设备很忙,有可能是CPU饥饿导致了ANR

如果CPU使用量很少,说明主线程被BLOCK了

如果IOwait很高,说明ANR有可能是主线程在进行I/O操作造成的

所以我这里导致ANR的原因应该是CPU不足。

仅仅查看log的信息还不足以帮我们定位到ANR的原因,所以需要去看data/anr/trace文件

at com.richeninfo.cmoa.widget.AutoScrollViewPager.onTouchEvent(AutoScrollViewPager.java:219)

从这些信息中首先看到线程的状态为”main” prio=5 tid=1 SUSPENDED

而经过一位朋友提示线程状态为SUSPENDED

只有在debug的时候会这样,可是我没在debug啊,所以网上查到下面资料图:

可以看到资料说这种状态通常是由于GC或者debug,所以我的情况应该是就GC了,这也验证了前面说的CPU不足的原因。

Reason: Input dispatching timed out(Waiting because the touched window has not finished processing the input events that were previously delivered to it.)

说明这CPU不足导致无法相应下一个input events导致ANR。

那就去看看onTouchEvent里都执行了什么鬼操作。

announcePager.setOnTouchListener(new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

announcePager.stopAutoScroll();

break;

case MotionEvent.ACTION_MOVE:

announcePager.startAutoScroll();

break;

case MotionEvent.ACTION_UP:

announcePager.startAutoScroll();

break;

default:

break;

}

return false;

}

});

可以看到相应了三个action,那么就去看看startAutoScroll()stopAutoScroll()都写了什么。

public void startAutoScroll(int delayTimeInMills) {

isAutoScroll = true;

sendScrollMessage(delayTimeInMills);

}

/**

* stop auto scroll

*/

public void stopAutoScroll() {

isAutoScroll = false;

handler.removeMessages(SCROLL_WHAT);

}

/**

* set the factor by which the duration of sliding animation will change

*/

public void setScrollDurationFactor(double scrollFactor) {

scroller.setScrollDurationFactor(scrollFactor);

}

private void sendScrollMessage(long delayTimeInMills) {

/** remove messages before, keeps one message is running at most **/

handler.removeMessages(SCROLL_WHAT);

handler.sendEmptyMessageDelayed(SCROLL_WHAT, delayTimeInMills);

}

看到是handler在发送消息并且每次发送之前都要把前面的消息移除。

结合我操作APP发生ANR的时机,判断问题应该是出现在action_move响应太频繁,导致频繁startAutoScroll();然后方法内部里频繁handler.removeMessages(),这样被remove的消息由于垃圾回收机制频繁引起GC,所以就导致了CPU不足,这样似乎可以验证前面的说法。

问题找到了,那就要解决,这个解决也简单,直接把action_move里的

startAutoScroll()注释掉就OK了,其实这里也不需要在action_move里执行startAutoScroll(),因为action_up里已经执行了startAutoScroll()。

这样ANR就分析解决完毕了。有了这次经验,以后相信自己能比较好应对ANR。。。认真分析trace信息和log日志.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值