ANR详细介绍

栏目: Android · 发布时间: 5年前

  • 1.ANR简单介绍
  • 2.ANR发生场景
  • 3.ANR发生的原理
  • 4.ANR有哪些具体案例
  • 5.ANR具体如何分析
  • 6.解决方案
  • 7.ANR问题解答

好消息

  • 博客笔记大汇总【16年3月到至今】,包括 Java 基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近20万字],转载请注明出处,谢谢!
  • 链接地址: github.com/yangchong21…
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

1.ANR简单介绍

  • 1.1 什么是ANR
    • ANR Activity not responding(页面没有响应)
    • ANR Application not responding 应用没有响应
    • Android 在4.0之后强制规定 访问网络必须开启子线程
    • 如果在主线程访问网络,4.0之后的系统就会抛出:android.os.NetworkOnMainThreadException 在主线程联网的异常
    • 联网的时候一定要在子线程操作,只要是耗时的操作,可能会把主线程阻塞住的操作,都要放到子线程里
    • 主线程(UI线程)16ms,刷新一次界面,一秒60次,60贞/秒
    • ANR(Application Not responding),是指应用程序未响应,Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR。
  • 1.2 ANR的产生需要满足三个条件
    • 主线程:只有应用程序进程的主线程响应超时才会产生ANR;
    • 超时时间:产生ANR的上下文不同,超时时间也会不同,但只要在这个时间上限内没有响应就会ANR;
    • 输入事件/特定操作:输入事件是指按键、触屏等设备输入事件,特定操作是指BroadcastReceiver和Service的生命周期中的各个函数,产生ANR的上下文不同,导致ANR的原因也会不同;

2.ANR发生场景

  • 主线程,被阻塞5秒钟以上,就会抛出ANR对话框。不同的组件发生ANR的时间不一样,Activity是5秒,BroadCastReceiver是10秒,Service是20秒(均为前台)。
  • 点击事件(按键和触摸事件)5s内没被处理: Input event dispatching timed out
  • service 前台20s后台200s未完成启动 Timeout executing service
    • Service Timeout是位于”ActivityManager”线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时触发。
    • 对于Service有两类:
      • 对于前台服务,则超时为SERVICE_TIMEOUT = 20s;
      • 对于后台服务,则超时为SERVICE_BACKGROUND_TIMEOUT = 200s
  • BroadcastReceiver的事件(onRecieve方法)在规定时间内没处理完(前台广播为10s,后台广播为60s):Timeout of broadcast BroadcastRecord
    • 以BroadcastReviever为例,在onRecieve()方法执行10秒内没发生第一种ANR(也就是在这个过程中没有输入事件或输入事件还没到5s)才会发生Receiver timeout,否则将先发生事件无相应ANR,所以onRecieve()是有可能执行不到10s就发生ANR的,所以不要在onRecieve()方法里面干活
  • ContentProvider的publish在10s内没进行完:timeout publishing content providers
  • 思考一下,比如service前台是20秒,后台是200秒没响应会导致ANR,那么这个时间是哪里来的呢?
    // How long we wait for a service to finish executing.
    static final int SERVICE_TIMEOUT = 20*1000;
    
    // How long we wait for a service to finish executing.
    static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
    
    mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
    
        void serviceTimeout(ProcessRecord proc) {
        String anrMessage = null;
    
        synchronized(mAm) {
            if (proc.executingServices.size() == 0 || proc.thread == null) {
                return;
            }
            final long now = SystemClock.uptimeMillis();
            final long maxTime =  now -
                    (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
            ServiceRecord timeout = null;
            long nextTime = 0;
            for (int i=proc.executingServices.size()-1; i>=0; i--) {
                ServiceRecord sr = proc.executingServices.valueAt(i);
                if (sr.executingStart < maxTime) {
                    timeout = sr;
                    break;
                }
                if (sr.executingStart > nextTime) {
                    nextTime = sr.executingStart;
                }
            }
            if (timeout != null && mAm.mLruProcesses.contains(proc)) {
                Slog.w(TAG, "Timeout executing service: " + timeout);
                StringWriter sw = new StringWriter();
                PrintWriter pw = new FastPrintWriter(sw, false, 1024);
                pw.println(timeout);
                timeout.dump(pw, "    ");
                pw.close();
                mLastAnrDump = sw.toString();
                mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
                mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
                anrMessage = "executing service " + timeout.shortName;
            } else {
                Message msg = mAm.mHandler.obtainMessage(
                        ActivityManagerService.SERVICE_TIMEOUT_MSG);
                msg.obj = proc;
                mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
                        ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
            }
        }
    
        if (anrMessage != null) {
            mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
        }
    }
    复制代码

3.ANR发生的原理

  • 关于ANR机制实现的原理可以先看这篇文章,个人觉得写的十分不错,可以看看: gityuan.com/2016/07/02/…
  • 大概原理如下:
    • 1.在进行相关操作调用hander.sendMessageAtTime()发送一个ANR的消息,延时时间为ANR发生的时间(如activity是当前时间5s之后)。
    • 2.进行相关的操作
    • 3.操作结束后向remove掉该条message。如果相关的操作在规定时间没有执行完成,该条message将被handler取出并执行,就发生了ANR。

4.ANR有哪些具体案例

  • Acitvity,Fragment中暴力相应点击事件有可能会导致ANR
  • 断点调试时,程序可能会出现ANR无限应
  • 主线程做了耗时操作,比如查询数据库数据导致ANR

5.ANR具体如何分析

  • ANR问题是由于主线程的任务在规定时间内没处理完任务,而造成这种情况的原因大致会有一下几点:
    • 主线程在做一些耗时的工作导致线程卡死
    • 主线程被其他线程锁
    • cpu被其他进程占用,该进程没被分配到足够的cpu资源。
  • 然后看anr日志。千万别说不知道在哪里看日志,在发生ANR的时候,系统会收集ANR相关的信息提供给开发者:首先在Log中有ANR相关的信息,其次会收集ANR时的CPU使用情况,还会收集trace信息,也就是当时各个线程的执行情况。trace文件保存到了/data/anr/traces.txt中
    • 从log中找到ANR反生的信息:会包含了ANR的时间、进程、是何种ANR等信息。
    • 在该条log之后会有CPU usage的信息,表明了CPU在ANR前后的用量(log会表明截取ANR的时间),从各种CPU Usage信息中大概可以分析如下几点:
      • 如果某些进程的CPU占用百分比较高,几乎占用了所有CPU资源,而发生ANR的进程CPU占用为0%或非常低,则认为CPU资源被占用,进程没有被分配足够的资源,从而发生了ANR。这种情况多数可以认为是系统状态的问题,并不是由本应用造成的。
      • 如果发生ANR的进程CPU占用较高,如到了80%或90%以上,则可以怀疑应用内一些代码不合理消耗掉了CPU资源,如出现了死循环或者后台有许多线程执行任务等等原因,这就要结合trace和ANR前后的log进一步分析了。
      • 如果CPU总用量不高,该进程和其他进程的占用过高,这有一定概率是由于某些主线程的操作就是耗时过长,或者是由于主进程被锁造成的。
    • 除了上述分析CPU usage之后,确定问题需要我们进一步分析trace文件。trace文件记录了发生ANR前后该进程的各个线程的stack。对我们分析ANR问题最有价值的就是其中主线程的stack,一般主线程的trace可能有如下几种情况:
      • 主线程是running或者native而对应的栈对应了我们应用中的函数,则很有可能就是执行该函数时候发生了超时。
      • 主线程被block:非常明显的线程被锁,这时候可以看是被哪个线程锁了,可以考虑优化代码。如果是死锁问题,就更需要及时解决了。
      • 由于抓trace的时刻很有可能耗时操作已经执行完了(ANR -> 耗时操作执行完毕 ->系统抓trace),这时候的trace就没有什么用了,主线程的stack就是这样的:
      • ANR详细介绍
  • 总结,就是两个问题
    • 1.CPU 问题
      • 在 Monkeylog.log 文件中定位到 "anr in" 位置,查看 cpu usage ,total 占用,如发现接近100%,暂时判断为 cpu 问题。
      • 然后在 logcat.log 文件中定位到 "not responding" 发生时间,并截取cpuinfo.log 中时间点前后 5s 的 log,然后计算 CPU 占中,看哪个进程用的多,在酌情分析模块的 CPU 占中。
    • 2.GC 问题
      • 定位到 logcat.log 文件中 "not responding" 发生时间点;
      • 去查看发生 ANR 时间点对应的 trace 文件,定位到应用报名,若Dalvik Thread主线程显示“SUSPENDED”,则为内存问题;
      • 截取 ANR 发生时间点前 5s 的 log,分析 "dalvikvm" 打印的 Paused GC 耗时,如果过多则定位为 GC 问题,需要查看这 5s 件发生了哪些耗时的操作。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web视觉设计

Web视觉设计

Penny McIntire / 叶永彬 / 机械工业出版社 / 2008-08 / 56.00元

本书系统全面地介绍Web页面外观设计的相关知识。本书分为八章:导论、站点分析、导航、页面布局、色彩、图形、排版和表单。全面讲解网站界面所涉及的内容,叙述生动,由浅入深,提供了大量的示例代码以具体地说明如何运用所讨论的设计概念。. 本书可供Web开发技术人员和美工人员参考。...一起来看看 《Web视觉设计》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器