一.ANR简介
ANR 全称 Applicatipon No Response。Android 设计 ANR 的用意,是系统通过与之交互的组件(Activity,Service,Receiver,Provider)以及用户交互(InputEvent)进行超时监控,以判断应用进程(主线程)是否存在卡死或响应过慢的问题。
系统在通过Binder通信向应用进程发送上述组件消息或 Input 事件时,在 AMS 或 Input 服务端同时设置一个异步超时监控。
针对不同类型事件,设置的超时时长也存在差别,以下是 Android 系统对不同类型的超时阈值设置。
二.ANR监控
1.ANR WatchDog检测
既然ANR的原因是进程在一定时间内没有响应,那么我们很自然地想到,向主线程发送一个任务,如果一段时间内没有被执行的话,就认为发生了 ANR。
弊端:不准确,超时时间不固定,有可能是5秒有可能是10秒。且周期也不一定同步。
2.ANR 信号监听
我们注意到当 ANR 发生时会发送 SIGQUIT 信号,那么我们通过监听这一信号不就可以实现 ANR 监控了吗?
默认情况下进程通过SignalCatcher监听SIGQUIT信号,进行堆栈转储生成 ANR Trace 文件。因此当我们监听SIGQUIT信号后,需要重新向SignalCatcher发送SIGQUIT。
如果缺少重新向 SignalCatcher 发送 SIGQUIT 信号的步骤,Android System 管理服务(AMS)将一直等待 ANR 进程写入堆栈信息。直到超过20秒的超时时间,AMS 才会被迫中断,并继续后续流程。这将导致 ANR 弹窗的显示非常缓慢(因为超时时间为20秒),同时在 /data/anr 目录下也无法生成完整的 ANR Trace 文件。
弊端:当监听到 SIGQUIT 信号时,不一定是发生了 ANR。当进程被标为 NOT_RESPONDING 时一定发生了 ANR,但是当进程发生了 ANR 时,不一定会被标记为 NOT_RESPONDING。
解决办法:监听到信号时再进行一次检查,在 ANR 弹窗前,会给发生 ANR 的进程标记一个 NOT_RESPONDING 的 flag,而这个 flag 我们可以通过 ActivityManager 来获取
代码:
/**
* 当前进程是否处于ANR状态
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static boolean isAnrErrorState(Application application) {
if (null == application) return false;
try {
ActivityManager activityManager = (ActivityManager) application.getSystemService(Context.ACTIVITY_SERVICE);
if (null != activityManager) {
List<ActivityManager.ProcessErrorStateInfo> processErrorStateInfoList = activityManager.getProcessesInErrorState();
if (null == processErrorStateInfoList) return false;
for (ActivityManager.ProcessErrorStateInfo proc : processErrorStateInfoList) {
if (null == proc || proc.pid != android.os.Process.myPid()) {
continue;
}
if (proc.condition != ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING) {
continue;
}
return true;
}
return false;
}
} catch (Throwable ignored) {
}
return false;
}
三. 获取 ANR Info
/**
* 获取ANR状态的进程shortMessage信息
*/
@SuppressLint("ServiceCast")
public static String getAnrShortMessage(Application application) {
if (null == application) return "";
ActivityManager.ProcessErrorStateInfo info = (ActivityManager.ProcessErrorStateInfo) application.getSystemService(Context.ACTIVITY_SERVICE);
return (null != info) ? info.shortMsg : "";
}
/**
* 获取ANR状态的进程longMsg信息
*/
@SuppressLint("ServiceCast")
public static String getAnrLongMsg(Application application) {
if (null == application) return "";
ActivityManager.ProcessErrorStateInfo info = (ActivityManager.ProcessErrorStateInfo) application.getSystemService(Context.ACTIVITY_SERVICE);
return (null != info) ? info.longMsg : "";
}