之前面试的时候,被一位技术总监问到这样一个问题:

如果让你去设计一套 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 有点相似。到这里。 我们先看两件事吧,

  1. mAm.mHandler 是怎么创建呢
  2. 这个消息怎么处理

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 的操作。


搬运地址:

理解 Android ANR 的触发原理