Android ANR机制介绍
ANR(Application Not responding),是指应用程序未响应,Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR。一般地,这时往往会弹出一个提示框,告知用户当前xxx未响应,用户可选择继续等待或者Force Close。
Android为什么要引入ANR
android ui 被设计成非线程安全的(个人认为主要是为了简单),这就要求满足两点:
- 不能在非ui线程中操作ui.
- 不能阻塞ui线程。
其中第二个就为ANR埋下了伏笔,既然不能阻塞ui线程,那么如果阻塞了,用户会得到什么交互呢,这就是ANR.告知用户应用程序发生了阻塞ui线程的操作,但是方式简单粗暴,继续等待或者Force Close。其实体验一点不好。这也就对我们程序员有了更高的要求,杜绝黄赌毒,杜绝ANR.
发生ANR场景
- Service Timeout : 服务前台在20s,后台10s内未执行完成。
- BroadcastQueue Timeout : 广播前台10s,后台60s内未执行完成。
- ContentProvider Timeout : 内容提供器10s未执行完成。
- InputDispatching Timeout: 输入事件分发超时,5s。 包含按键与触屏。
// 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; // 后台
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000; // 前台
static final int BROADCAST_BG_TIMEOUT = 60*1000; // 后台
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
// How long we wait for an attached process to publish its content providers
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
注意:
android的四大组件默认都在主线程中运行的,特别提醒,Service和Broadcast组件也是在主线程中运行的。不要以为Service是在后台运行的,就不在主线程运行。Android提IntentService可以不在主线程执行任务,看源码就会发现其实关联一个HandlerThread.
顺便说一下,Handler默认也是在主线程中运行的,持有的main thread的looper.如果让handler不在主线程执行,需要绑定其它线程的looper.
ANR处理
java层的处理:
所有的java进程都运行在java虚拟机中,当应用发生ANR时,其最终的一个环节是向目标进程通过kill - 3,发送信号SIGNAL_QUIT.Android进程收到SIGQUIT时,虚拟机会捕获这个信号,并输出相应的traces信息,保存到/data/anr/traces.txt中。native层的处理:
native进程在发生ANR时,debuggerd服务接收到系统发来的DEBUGGER_ACTION_DUMP_BACKTRACE命令,最后通过dump_backtrace()将日志输出到/data/anr/traces.txt。
CPU和内存的关键指标含义
CPU的使用时间:读取/proc/stat
- user: 用户进程的cpu使用时间
- nice: 降低过优先级进程的cpu的使用时间,linux进程都是有优先级的,这个优先级可以进行动态调整,譬如进程初始化优先级的值设为10,运行时降低为8,那么修正值为-2就是nice.android系统将user和nice这两个时间归类成user.
- sys : 内核进程的cpu使用时间
- idle: cpu等待IO的时间
- hw irq : 硬件中断的时间,如果外设出现故障,需要通过硬件终端通知CPU保存现场,发生上下文切换时间就是CPU的硬件中断时间。
- sw irq: 软件中断的时间,同硬件中断一样,如果软件要求CPU中断,同上下文切换时间就是CPU的软件中断时间。
CPU负载:读取/proc/loadavg
统计最近1分钟、5分钟、15分钟内cpu的平均活动进程数。CPU的负载就是要处理的任务数。
手机内存:读取/proc/meminfo
dumpsys meminfo命令的输出结果分以下4部分:
序列 | 类型 | 排序 | 解释 |
---|---|---|---|
1 | process | PSS | 以进程的PSS从大到小依次排序显示,每行显示一个进程 |
2 | oom adj | PSS | Native/System/Persistent/Foreground/Visible/Perceptible/A Services/Home/B Services/Cached,分别显示每类的进程情况;一般而言,占用内存越多,oom_adj就越大,也就越有可能被选中。OOM异常的时候会根据这个表单从下往上杀掉进程释放内存 |
3 | category | PSS | 以Dalvik/Native/.art mmap/.dex map等划分的各类进程的总PSS情况 |
4 | total | 总内存、剩余内存、可用内存、其他内存 |
root@phone:/ # cat /proc/meminfo
MemTotal: 883248 kB //RAM可用的总大小 (即物理总内存减去系统预留和内核二进制代码大小)
MemFree: 62136 kB //RAM未使用的大小
MemAvailable: 192336 kB
Buffers: 1648 kB //用于文件缓冲
Cached: 175404 kB //用于高速缓存
SwapCached: 5392 kB //用于swap缓存
对于cache和buffer也是系统可以使用的内存。所以系统当前总的可用内存为 MemFree+Buffers+Cached。