美文网首页
Android ANR问题专题

Android ANR问题专题

作者: 码农朱同学 | 来源:发表于2019-04-15 20:07 被阅读0次

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
  1. 使用AsyncTask处理耗时IO操作
  2. 使用Thread(不可以创建handler)或者HandlerThread提高优先级
  3. 使用Handler来处理工作线程的耗时任务
  4. 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文件,


看完这篇 Android ANR 分析,就可以和面试官装逼了!
https://juejin.im/entry/5c3e9ef6e51d4539b927dfd1

  1. ANR的监测机制:首先分析Service和输入事件大致工作流程,然后从Service,InputEvent两种不同的ANR监测机制的源码实现开始,分析了Android如何发现各类ANR。在启动服务、输入事件分发时,植入超时检测,用于发现ANR。
  2. ANR的报告机制:分析Android如何输出ANR日志。当ANR被发现后,两个很重要的日志输出是:CPU使用情况和进程的函数调用栈,这两类日志是我们解决ANR问题的利器。
  3. 监测ANR的核心原理是消息调度和超时处理。
  4. 只有被ANR监测的场景才会有ANR报告以及ANR提示框。

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

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

(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超时监测整体流程如下图

输入事件超时监测


记一次分析解决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日志.

相关文章

  • Android ANR问题专题

    android ANR发生的原因总结和解决办法 ANR的全称是application not responding...

  • 查看ANR日志

    标签(空格分隔):Android改Bug技巧 【Android】抓取log(anr) 1、anr问题的log一般都...

  • ANR系列

    ANR(0)---理解Android ANR的触发原理ANR(1)---理解Android ANR的信息收集过程A...

  • Android ANR问题

    参考文章:https://www.jianshu.com/p/fa962a5fd939 参考文章:https://...

  • ANR监控方案总结

    1.前言 ANR比较棘手在于,没有崩溃日志,定位问题比较困难,而且ANR是必须要解决的问题。 Android对AN...

  • [068]破局ANR

    前言 ANR是Android中经常遇到的问题,常规的ANR问题,一般可以通过adb日志和trace文件,找到导致A...

  • BAT 大厂Android研发岗必刷真题:Android异常与性

    今天来讲一讲在面试中碰到的Android异常与性能优化相关问题: 1、anr异常面试问题讲解 a) 什么是anr...

  • Android ANR详解

    标签 :Android ANR traces文件 1、ANR定义及分类 ANR:Application Not R...

  • 记录一次ANR的分析过程

    Android开发中难免碰到ANR问题,但是网上关于ANR的文章并不多,那我就写一篇最近工作中碰到ANR的分析过程...

  • Android ANR(二)-触发原理

    接之前的文章:Android ANR问题(一)-基本分析方法,这篇文章总结一下ANR触发原理。 一、Service...

网友评论

      本文标题:Android ANR问题专题

      本文链接:https://www.haomeiwen.com/subject/ytwpwqtx.html