源码分析 - ANR 的机制
之前面试的时候,被一位技术总监问到这样一个问题:
如果让你去设计一套 ANR 的机制,你怎么做? 你可以修改 Android 任何源码的。怎么能做到自定义 ANR 的时间,然后在 ANR 发送的时候,弹出相应的对话框提示 ANR 了。 ANR 机制在哪里进行监听?
这一下子就把我问住了,虽然我也想了几种方式,可是还是没说到点子上, 回来就只好查资料,看源码了。原来是通过一个 HandlerThread ,在一个子线程中开启一个 Handler ,然后在主线程开始执行某些方法的时候,发送一个子线程的 handler 的延迟消息,如果规定的时间内,没有把这个消息 remove ,也就是执行到这个消息,那么就要弹出 ANR 提示框。
我们知道,产生 ANR 的场景有以下几种:
- Service Timeout:比如前台服务在 20s 内未执行完成;
- BroadcastQueue Timeout:比如前台广播在 10s 内未执行完成 Provider Timeout:内容提供者,在 publish 过超时10s;
- InputDispatching Timeout: 输入事件分发超时 5s ,包括按键和触摸事件。
那么我们就从 Service 20 s 说起把
我们知道,启动 Service 的时候,最终会执行到 ActiveServices 中的 realStartServiceLocked() 方法,详情查看 Android 四大组件之 Service, ANR 就和这个方法有关。
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app ,
boolean execInFg) throws RemoteException {
...
//发送 delay 消息(SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg , "create");
//创建 Service 对象,并且调用onCreat()
app.thread.scheduleCreateService(r, r.serviceInfmAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState);
...
}
bumpServiceExecutingLocked() 其实就是发送延迟消息。
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg , String why) {
...
scheduleServiceTimeoutLocked(r.app);
...
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
long now = SystemClock.uptimeMillis();
Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg ? (now + SERVICE_TIMEOUT) : (now SERVICE_BACKGROUND_TIMEOUT));
}
看到没,通过mAm.mHandler 发送一个延迟消息,这个延迟消息是多少呢, 20s ,
static final int SERVICE_TIMEOUT = 20 * 1000;
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
消息类型是 AMS 中的 SERVICE_TIMEOUT_MSG ,看名字 Service timeout 了,感觉和 ANR 有点相似。到这里。 我们先看两件事吧,
- mAm.mHandler 是怎么创建呢
- 这个消息怎么处理
mAm.mHandler 的创建
mAm 就是 AMS 对象,
//ActiveServices.java
final ActivityManagerService mAm;
所以我们需要在 AMS 中找 mHandler 是怎么创建的。 在 AMS 的构造方法中,我们找到的创建的地方
public ActivityManagerService(Context systemContext) {
mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
mHandlerThread.start();
mHandler = new MainHandler(mHandlerThread.getLooper());
...
}
而这个 ServiceThread 是继承 HandlerThread 的,而 HandlerThread 我们之前讲过会创建一个子线程的 Looper 对象,,AsyncTaks , HandlerThread , IntentService 小结。而 mHander 创建的时候,传递的是这个mHandlerThread.getLooper() ,所以这就可以说明,这个 mHandler 是一个子线程的 handler ,创建子线程的 Handler 对象有啥作用呢,用来监听主线程的 ANR 。
SERVICE_TIMEOUT_MSG 怎么处理
final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null , true);
}
@Override
public void handleMessage(Message msg) {
...
case SERVICE_TIMEOUT_MSG: {
if (mDidDexOpt) {
mDidDexOpt = false;
Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
nmsg.obj = msg.obj;
mHandler.sendMessageDelayed(nmsg, ActiveServices.SERVICE_TIMEOUT);
return;
}
mServices.serviceTimeout((ProcessRecord) msg.obj);
}
...
}
会执行到 mServices.serviceTimeout()中
//ActiveServices
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
...
if (anrMessage != null) {
mAm.appNotResponding(proc, null , null , false , anrMessage);
}
}
//AMS
final void appNotResponding(ProcessRecord app, ActivityRecord activity , ActivityRecord parent
, boolean aboveSystem , final String annotation) {
...
//生成最新的 trace 文件
File tracesFile = dumpStackTraces(true, firstPids , processCpuTracker , lastPids , NATIVE_STACKS_OF_INTEREST);
...
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
HashMap<String, Object> map = new HashMap<String, Object>();
msg.what = SHOW_NOT_RESPONDING_MSG;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
map.put("app", app);
if (activity != null) {
map.put("activity", activity);
}
mHandler.sendMessage(msg);
}
}
生成最新的 trace 文件,然后发送 SHOW_NOT_RESPONDING_MSG 消息,
case SHOW_NOT_RESPONDING_MSG: {
...
if (mShowDialogs) {
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this, mContext , proc ,
(ActivityRecord) data.get("activity"), msg.arg1 != 0);
d.show();
proc.anrDialog = d;
...
}
//AppNotRespondingDialog.java
final class AppNotRespondingDialog extends BaseErrorDialog {
public AppNotRespondingDialog(ActivityManagerService service, Context context ,
ProcessRecord app , ActivityRecord activity , boolean aboveSystem) {
...
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.setTitle("Application Not Responding: " + app.info.processName);
attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
}
}
BaseErrorDialog extends AlertDialog
Application Not Responding: 这个应该熟悉吧,就是 ANR 的提示框。这样就弹出应用程序无响应 Dialog 了
移除 ANR 消息
可是还有一个问题啊,什么时候移除 ANR 呢
void publishServiceLocked(ServiceRecord r, Intent intent , IBinder service) {
...
// c的类型是ConnectionRecord c.conn的类是ServiceDispatcher.InnerConnection
c.conn.connected(r.name, service);
...
serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
...
}
在执行到c.conn.connected(r.name, service);之后,这个其实最终就会执行到 Service 的 onServiceConnected() 方法
我们发现了 serviceDoneExecutingLocked() 这个方法
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying , boolean finishing) {
...
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
...
}
在这里面,会 remove 这个消息,如果 ANR 规定时间内,结束这个流程,那么移除掉,就不会弹出 ANR 对话框了,如果没有在规定时间执行完,那么就会执行 ANR 的操作。
搬运地址:
既已览卷至此,何不品评一二: