• Android探究之ANR


    什么是ANR

    ANR:Application Not Responding,即应用程序无响应。

    在Android中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会监测应用程序的响应时间,如果应用程序主线程(即UI线程)在超时时间内对输入事件没有处理完毕,或者对特定操作没有执行完毕,就会出现ANR。

    对于输入事件没有处理完毕产生的ANR,Android会显示一个对话框,提示用户当前应用程序没有响应,用户可以选择继续等待或者关闭这个应用程序(也就是杀掉这个应用程序的进程)。

    为什么会产生ANR

    ANR一般有三种类型:

    1. 输入事件(按键和触摸事件)5s内没被处理:Input event dispatching timed out
    2. BroadcastReceiver的事件(onRecieve方法)在规定时间内没处理完(前台广播为10s,后台广播为60s):Timeout of broadcast BroadcastRecord
    3. Service前台20s后台200s未完成启动:Timeout executing service
    4. ContentProvider的publish在10s内没进行完:timeout publishing content providers

    ANR产生的常见原因:

    1. 主线程(UI线程)在做一些阻塞耗时的工作。例如文件读写,数据库读写,网络查询等。
    2. 主线程被其他线程锁。
    3. cpu被其他进程占用,该进程没被分配到足够的cpu资源。

    如何分析ANR

    从log中找到ANR发生的信息

    可以从log中搜索“ANR in”或“am_anr”,会找到ANR发生的log,该行会包含了ANR的时间、进程、是何种ANR等信息。

    分析ANR产生的trace文件

    ANR产生时,系统会生成一个traces.txt的文件放在/data/anr/下。 可以通过adb命令将其导出到本地:
    adb pull data/anr/traces.txt

    trace文件记录了发生ANR前后该进程的各个线程的stack。

    分析思路
    1. 普通阻塞导致的ANR。
    2. 如果是BroadcastReceiver的ANR可以怀疑BroadCastReceiver.onRecieve()的问题,如果的Service或Provider就怀疑是否其onCreate()的问题。
    3. 如果某些进程的CPU占用百分比较高,几乎占用了所有CPU资源,而发生ANR的进程CPU占用为0%或非常低,则认为CPU资源被占用,进程没有被分配足够的资源,从而发生了ANR。这种情况多数可以认为是系统状态的问题,并不是由本应用造成的。
    4. 如果CPU使用量很少,说明主线程被BLOCK了,可能是主进程被锁。
    5. 如果IOwait很高,说明ANR有可能是主线程在进行I/O操作造成的。
    6. 如果CPU使用量接近100%,说明CPU满负荷,有可能是CPU饥饿导致了ANR。
    7. 内存原因。

    如何避免ANR

    1.合理使用UI主线程,耗时操作放入其他线程工作。

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

    1.2 耗时的工作(比如数据库操作,I/O,连接网络或者别的有可能阻碍UI线程的操作)把它放入单独的线程处理。

    2.尽量用Handler来处理UI thread和别的thread之间的交互。

    3.合理使用并遵循Android生命周期,避免在onCreate()和onResume()做过多的事情。

    4.sharedPreference的使用:

    4.1 sharedPreference的commit()方法是同步的,apply()方法一般是异步执行的。在主线程不要用其commit(),用apply()替换。

    4.2 sharedPreference的写是全量写而非增量写,所以尽量都修改完同一apply,避免改一点apply一次(apply()方法在Activity stop的时候主线程会等待写入完成,提交多次就很容易卡)。并且存储文本也不宜过大,这样会很慢。另外,如果写入的是json或者xml,由于需要加和删转义符号,速度会比较慢。

    5.如果主线程阻塞,开辟单独的子线程来处理耗时阻塞事务。

    6.如果I/O阻塞,一般来说就是文件读写或数据库操作执行在主线程了,可以通过开辟子线程的方式异步执行。

    7.如果内存不够用,增大VM内存,使用largeHeap属性,排查内存泄露。

    拓展

    哪些地方是执行在主线程的

    各个组件的生命周期函数都不应该有太耗时的操作。

    Activity的所有生命周期回调、Service的onCreate()、BroadcastReceiver的onReceive(开个IntentService去执行相应操作)、
    ContentProvider的onCreate()是执行在主线程的。

    没有使用子线程的looper的Handler的handleMessage,
    post(Runnable)是执行在主线程的。

    AsyncTask的回调中除了doInBackground,其他都是执行在主线程的。

    View的post(Runnable)是执行在主线程的。

    尽量避免主线程的被锁的情况。

    一些同步的操作主线程有可能被锁,需要等待其他线程释放相应锁才能继续执行,这样会有一定的ANR风险。对于这种情况有时也可以用异步线程来执行相应的逻辑。另外, 我们要避免死锁的发生(主线程被死锁基本就等于要发生ANR了)。

  • 相关阅读:
    跨域
    关于DEDECMS自定义模型当中添加自定义字段后在后台添加内容后不显示解决方案
    js复制文本
    dedecms 织梦显示时间格式
    基本特效:饿了么丝滑无缝过度搜索栏的实现
    2016-wing的年度总结
    这交互炸了(四) :一分钟让你拥有微信拖拽透明返回PhotoView
    这交互炸了(三):闪屏页是像云一样消失的
    这交互炸了(二):爱范儿是如何让详情页缩小为横向列表的
    这交互炸了:饿了么是怎么让Image变成详情页的
  • 原文地址:https://www.cnblogs.com/juneyu/p/10066179.html
Copyright © 2020-2023  润新知