ANR

386 阅读28分钟

ANR

ANR(Application Not Responding),应用程序无响应。ANR 属于应用程序的范畴,ANR 由消息处理机制保证,Android 在系统层实现了一套精密的机制来发现 ANR,核心原理是消息调度超时处理

其次,ANR 机制主体实现在系统层。所有与 ANR 相关的消息,都会经过系统进程(system_server)调度,然后派发到应用进程完成对消息的实际处理,同时,系统进程设计了不同的超时限制来跟踪消息的处理。一旦应用程序处理消息不当,超时限制就起作用了,它收集一些系统状态,例如:CPU/IO使用情况、进程函数调用栈,并且报告用户有进程无响应了(ANR 对话框)。

然后,ANR 问题本质是一个性能问题。ANR 机制实际上对应用程序主线程的限制,要求主线程在限定的时间内处理完一些最常见的操作(启动服务、处理广播、处理输入),如果处理超时,则认为主线程已经失去了响应其他操作的能力。主线程中的耗时操作,例如:密集CPU运算、大量IO、复杂界面布局等,都会降低应用程序的响应能力。

ANR 机制

ANR 机制可以分为两部分:

ANR 的监测:Android 对于不同的 ANR 类型(Broadcast,Service,InputEvent)都有一套监测机制。

ANR 的报告:在监测到 ANR 以后,需要显示 ANR 对话框、输出日志(发生 ANR 时的进程函数调用栈、CPU 使用情况等)。

ANR 的触发原因

谷歌文档中对 ANR 产生的原因是这么描述的:

Android 系统中的应用被 ActivityManagerService 及 WindowManagerService 两个系统服务监控着,系统会在如下两种情况展示出 ANR 的对话框!

KeyDispatchTimeout ( 5 seconds ) :按键或触摸事件在特定时间内无响应

BroadcastTimeout ( 10 seconds ):BroadcastReceiver 在特定时间内无法处理完成

ServiceTimeout ( 20 seconds ) :Service 在特定的时间内无法处理完成

Service 超时监测机制

Service 运行在应用程序的主线程,如果 Service 的执行时间超过 20 秒,则会引发 ANR。

当发生 Service ANR 时,一般可以先排查一下在 Service 的生命周期函数中有没有做耗时的操作,例如复杂的运算、IO 操作等。如果应用程序的代码逻辑查不出问题,就需要深入检查当前系统的状态:CPU 的使用情况、系统服务的状态等,判断当时发生 ANR 进程是否受到系统运行异常的影响。

系统是如何检测 Service 超时的呢?Android 是通过设置定时消息实现的。定时消息是由 AMS 的消息队列处理的,AMS 有 Service 运行的上下文信息,所以在 AMS 中设置一套超时检测机制也是合情合理的。

Service ANR 机制相对最为简单,主体实现在ActiveServices中。

在 Service 的启动流程中,Service 进程 attach 到 system_server 进程后会调用 realStartServiceLocked() 方法。

// frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

public final class ActiveServices {

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {

        // 发送 delay 消息(SERVICE_TIMEOUT_MSG)
        bumpServiceExecutingLocked(r, execInFg, "create");

        boolean created = false;
        try {
            
            // 最终执行服务的 onCreate() 方法
            app.thread.scheduleCreateService(r, r.serviceInfo, mAm.
                compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);

            ... ...
        }
    }
    
}
private final void bumpServiceExecutingLocked(...) {

    scheduleServiceTimeoutLocked(r.app);
    
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return;
    }
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    // 当超时后仍没有 remove 该 SERVICE_TIMEOUT_MSG 消息,
    // 通过 AMS.MainHandler 抛出一个定时消息。
    mAm.mHandler.sendMessageDelayed(msg,
            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

AMS.MainHandler 抛出一个定时消息 SERVICE_TIMEOUT_MSG。

前台进程中执行 Service,超时时间是 SERVICE_TIMEOUT(20 秒)

    // How long we wait for a service to finish executing.
    static final int SERVICE_TIMEOUT = 20*1000;

后台进程中执行 Service,超时时间是 SERVICE_BACKGROUND_TIMEOUT(200 秒)

// How long we wait for a service to finish executing.
    static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

当 Service 的生命周期结束时(不会 ANR),会调用 serviceDoneExecutingLocked() 方法,之前抛出的 SERVICE_TIMEOUT_MSG 消息在这个方法中会被清除。

void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
    boolean inDestroying = mDestroyingServices.contains(r);
    if (r != null) {
        ... ...        
        serviceDoneExecutingLocked(r, inDestroying, inDestroying);
    }
}

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
          boolean finishing) {    
    ... ...
    if (r.executeNesting <= 0) {
        if (r.app != null) {    
            ... ...
            // 当前服务所在进程中没有正在执行的service,清除 SERVICE_TIMEOUT_MSG 消息
            if (r.app.executingServices.size() == 0) {
                mAm.mHandler.removeMessages(
                             ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);            
                ... ...
            }
    ... ...
}

如果没有 Remove 掉 SERVICE_TIMEOUT_MSG,在 system_server 进程中有一个 Handler 线程,名叫 ActivityManager。

如果在超时时间内,SERVICE_TIMEOUT_MSG 没有被清除,便会向该 Handler 线程发送一条信息 SERVICE_TIMEOUT_MSG。

final class MainHandler extends Handler {
    ... ...

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            ... ...
            
            case SERVICE_TIMEOUT_MSG: {
                mServices.serviceTimeout((ProcessRecord)msg.obj);
            } break;
        ... ...
    }
}
void serviceTimeout(ProcessRecord proc) {
    String anrMessage = null;

    synchronized(mAm) {
        ... ...
        
        long nextTime = 0;

        // 寻找运行超时的 Service
        for (int i = proc.executingServices.size() - 1; i >= 0; i--) {
            ServiceRecord sr = proc.executingServices.valueAt(i);
            if (sr.executingStart < maxTime) {
                timeout = sr;
                break;
            }
            if (sr.executingStart > nextTime) {
                nextTime = sr.executingStart;
            }
        }

        // 判断执行 Service 超时的进程是否在最近运行进程列表,如果不在,则忽略这个 ANR
        if (timeout != null && mAm.mLruProcesses.contains(proc)) {
            Slog.w(TAG, "Timeout executing service: " + timeout);
            StringWriter sw = new StringWriter();
            PrintWriter pw = new FastPrintWriter(sw, false, 1024);
            pw.println(timeout);
            timeout.dump(pw, "    ");
            pw.close();
            mLastAnrDump = sw.toString();
            mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
            mAm.mHandler.postDelayed(mLastAnrDumpClearer, 
                                           LAST_ANR_LIFETIME_DURATION_MSECS);
            anrMessage = "executing service " + timeout.shortName;
        ... ...
    }

    if (anrMessage != null) {
        // 当存在 timeout 的 service,则执行 appNotResponding
        mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
    }
}

上述方法会找到当前进程已经超时的 Service,经过一些判定后,决定要报告 ANR,最终调用 AMS.appNotResponding() 方法。

走到这一步,ANR 机制已经完成了监测报告任务,剩下的任务就是 ANR 结果的输出,我们称之为 ANR 的报告机制。ANR 的报告机制是通过 AMS.appNotResponding() 完成的,Broadcast 和 InputEvent 类型的 ANR 最终也都会调用这个方法。

Broadcast 超时处理

应用程序可以注册广播接收器,实现 BroadcastReceiver.onReceive() 方法来完成对广播的处理。通常,这个方法是在主线程执行的,Android 限定它执行时间不能超过10秒,否则,就会引发 ANR。

onReceive() 也可以调度在其他线程执行,通过 Context.registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) 这个方法注册广播接收器,可以指定一个处理的 Handler,将 onReceive() 调度在非主线程执行。

广播消息的调度

AMS 维护了两个广播队列 BroadcastQueue:

foreground queue,前台队列的超时时间是 10 秒。

background queue,后台队列的超时时间是 60 秒。

之所以有两个,就是因为要区分不同超时时间。所有发送的广播都会进入到队列中等待调度,在发送广播时,可以通过 Intent.FLAG_RECEIVER_FOREGROUND 参数将广播投递到前台队列。

AMS 会不断地从队列中取出广播消息派发到各个 BroadcastReceiver。当要派发广播时,AMS 会调用 BroadcastQueue.scheduleBroadcastsLocked() 方法!

public final class BroadcastQueue { 
    public void scheduleBroadcastsLocked() { 
        if (mBroadcastsScheduled) { return; } 
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this)); 
        mBroadcastsScheduled = true; 
    } 
}

上述方法中,往 AMS 的消息队列发送 BROADCAST_INTENT_MSG 消息,由此也可以看到真正派发广播的是 AMS 线程。由于上述方法可能被并发调用,所以通过 mBroadcastsScheduled 这个变量来标识 BROADCAST_INTENT_MSG 是不是已经被 AMS 接收了,当已经抛出的消息还未被接受时,不需要重新抛出。

public final class BroadcastQueue { 
    private final class BroadcastHandler extends Handler { 
        public BroadcastHandler(Looper looper) { 
            super(looper, null, true); 
            
        } 
        
        @Override public void handleMessage(Message msg) { 
            switch (msg.what) { 
                case BROADCAST_INTENT_MSG: {             
                    processNextBroadcast(true); 
                } break; 
                case BROADCAST_TIMEOUT_MSG: { 
                synchronized (mService) { 
                    broadcastTimeoutLocked(true); 
                } } break; 
                
            } } } }

接下来调用 BroadcastQueue.processNextBroadcast() 方法,参数为 true 表示这是一次来自 BROADCAST_INTENT_MSG 消息的派发请求。

阶段 1:处理并行广播信息

final void processNextBroadcast(boolean fromMsg) {         
    synchronized(mService) { 
        BroadcastRecord r; 
        ... ... 
        //阶段一:处理非串行广播消息 
        ① 设置mBroadcastsScheduled 
        
        if (fromMsg) { 
        // fromMsg 参数为 true 表示这是一次来自 BROADCAST_INTENT_MSG 消息的派发请求 
            mBroadcastsScheduled = false; 
        } 
        ② 处理“并行广播消息” 
        
        while (mParallelBroadcasts.size() > 0) { 
            ... ... 
            final int N = r.receivers.size(); 
            
            for (int i=0; i<N; i++) { 
                Object target = r.receivers.get(i); 
                
                ... ... 
                
                deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i); 
                
            } 
            
            addBroadcastToHistoryLocked(r); 
            
            ... ... 
            
        }  
        
        // ③ 处理阻塞的广播消息 
        
        if (mPendingBroadcast != null) {
        ... ... 
            if (!isDead) { 
            // It's still alive, so keep waiting // isDead表示当前广播消息的进程的存活状态 // 如果还活着,则返回该函数,继续等待下次派发 
            return; 
                
            } 
            ... ... 
            
        }

1、设置mBroadcastsScheduled

该变量在前文说过,是对 BROADCAST_INTENT_MSG 进行控制。 如果是响应 BROADCAST_INTENT_MSG 的派发调用,则将 mBroadcastsScheduled 设为false, 表示本次 BROADCAST_INTENT_MSG 已经处理完毕,可以继续抛出下一次 BROADCAST_INTENT_MSG 消息了。

2、处理“并行广播消息”

我们知道广播接收器有“动态”和“静态”之分,通过 Context.registerReceiver() 注册的广播接收器为“动态”的,通过 AndroidManifest.xml 注册的广播接收器为“静态”的。

广播消息有“并行”和“串行”之分,“并行广播消息”都会派发到“动态”接收器,“串行广播消息”则会根据实际情况派发到两种接收器。

在BroadcastQueue维护着两个队列:

mParallelBroadcasts:“并行广播消息”都会进入到此队列中排队。“并行广播消息”可以一次性派发完毕,即在一个循环中将广播派发到所有的“动态”接收器。

mOrderedBroadcasts:“串行广播消息”都会进入到此队列中排队。“串行广播消息”需要轮侯派发,当一个接收器处理完毕后,会再抛出 BROADCAST_INTENT_MSG 消息,再次进入 BroadcastQueue.processNextBroadcast() 处理下一个。

3、处理阻塞的广播消息

有时候会存在一个广播消息派发不出去的情况,这个广播消息会保存在 mPendingBroadcast 变量中。新一轮的派发启动时,会判断接收该消息的进程是否还活着,如果接收进程还活着,那么就继续等待。否则,就放弃这个广播消息。

阶段 2:处理串行广播信息

接下来是最为复杂的一部分,处理“串行广播消息”,ANR 监测机制只在这一类广播消息中才发挥作用,也就是说“并行广播消息”是不会发生ANR的。

final void processNextBroadcast(boolean fromMsg) {     
    synchronized(mService) { 
        BroadcastRecord r; 
        ... ...
        阶段一:处理并行广播消息 
        阶段二:处理串行广播消息 
        
        boolean looped = false; 
        do { 
            ... ... 
            r = mOrderedBroadcasts.get(0); 
            boolean forceReceive = false; 
            int numReceivers = (r.receivers != null) ? r.receivers.size() : 0; 
            // ① 广播消息的第一个ANR监测机制 
            if (mService.mProcessesReady && r.dispatchTime > 0) { 
                long now = SystemClock.uptimeMillis(); 
                if ((numReceivers > 0) && (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers)) &&  !mService.mAnrManager.isAnrDeferrable()) {     
                    broadcastTimeoutLocked(false); 
                    // forcibly finish this broadcast 
                    forceReceive = true; 
                    r.state = BroadcastRecord.IDLE; 
                } 
            } 
            ... ...  
            // ② 判断该广播消息是否处理完毕 
            if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) { 
            ... ... 
            cancelBroadcastTimeoutLocked(); 
            ... ... 
            mOrderedBroadcasts.remove(0); 
            r = null; 
            looped = true; 
            continue; 
            } 
        } while (r == null); 
    }

这部分是一个 do-while 循环,每次都从 mOrderedBroadcasts 队列中取出第一条广播消息进行处理。第一个 Broadcast ANR 监测机制千呼万唤总算是出现了:

1、判定当前时间是否已经超过了 r.dispatchTime + 2×mTimeoutPeriod×numReceivers。

dispatchTime 表示这一系列广播消息开始派发的时间。“串行广播消息”是逐个接收器派发的,一个接收器处理完毕后,才开始处理下一个消息派发。开始派发到第一个接收器的时间就是 dispatchTime。 dispatchTime 需要开始等广播消息派发以后才会设定,也就是说,第一次进入processNextBroadcast()时,dispatchTime=0,并不会进入该条件判断。

mTimeoutPeriod 由当前 BroadcastQueue 的类型决定(forground为10秒,background为60秒)。这个时间在初始化 BroadcastQueue 的时候就设置好了,本意是限定每一个 Receiver 处理广播的时间,这里利用它做了一个超时计算。

2、如果广播消息已经处理完毕,则从mOrderedBroadcasts中移除,重新循环,处理下一条;否则,就会跳出循环。

阶段 3:设定定时消息

以上代码块完成的主要任务是从队列中取一条“串行广播消息”,接下来就准备派发了。

final void processNextBroadcast(boolean fromMsg) {         
    synchronized(mService) { 
        BroadcastRecord r; 
        ... ...  
        阶段一:处理并行广播消息 
        阶段二:处理串行广播消息 
        阶段三:设定定时消息 
        ... ... 
        // 串行广播消息的第二个ANR监测机制 
        r.receiverTime = SystemClock.uptimeMillis(); 
        ... ... 
        if (! mPendingBroadcastTimeoutMessage) { 
            long timeoutTime = r.receiverTime + mTimeoutPeriod; 
            setBroadcastTimeoutLocked(timeoutTime); 
        }

取出“串行广播消息”后,一旦要开始派发,第二个ANR检测机制就出现了。

mPendingBroadcastTimeoutMessage 变量用于标识当前是否有阻塞的超时消息,如果没有则调用 BroadcastQueue.setBroadcastTimeoutLocked():

final void setBroadcastTimeoutLocked(long timeoutTime) { 
    if (! mPendingBroadcastTimeoutMessage) { 
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this); 
        mHandler.sendMessageAtTime(msg, timeoutTime); 
        mPendingBroadcastTimeoutMessage = true; 
        mService.mAnrManager.sendBroadcastMonitorMessage(timeoutTime, mTimeoutPeriod); 
    }
}

通过设置一个定时消息 BROADCAST_TIMEOUT_MSG 来跟踪当前广播消息的执行情况,这种超时监测机制跟 Service ANR 很类似,也是抛到 AMS 线程的消息队列。如果所有的接收器都处理完毕了,则会调用 cancelBroadcastTimeoutLocked() 清除该消息;否则,该消息就会响应,并调用 broadcastTimeoutLocked(),这个方法在第一种 ANR 监测机制的时候调用过,第二种 ANR 监测机制也会调用。

阶段 4:向“动态”接收器派发广播消息

设置完定时消息后,就开始派发广播消息了,首先是“动态”接收器:

final void processNextBroadcast(boolean fromMsg) {     
    synchronized(mService) { 
        BroadcastRecord r; 
        ... ...  
        阶段一:处理并行广播消息 
        阶段二:处理串行广播消息 
        阶段三:设定定时消息 
        阶段四:向“动态”接收器派发广播消息 
        final BroadcastOptions brOptions = r.options; 
        final Object nextReceiver = r.receivers.get(recIdx); 
        // 动态接收器的类型都是BroadcastFilter 
        if (nextReceiver instanceof BroadcastFilter) { 
        BroadcastFilter filter = (BroadcastFilter)nextReceiver; 
        deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
        ... ... 
        return; 
}

“动态”接收器的载体进程一般是处于运行状态的,所以向这种类型的接收器派发消息相对简单,调用 BroadcastQueue.deliverToRegisteredReceiverLocked() 完成接下来的工作。

阶段 5:向“静态”接收器派发广播消息

“静态”接收器是在AndroidManifest.xml中注册的,派发的时候,可能广播接收器的载体进程还没有启动,所以,这种场景会复杂很多。

final void processNextBroadcast(boolean fromMsg) {     
    synchronized(mService) { 
        BroadcastRecord r; 
        ... ...  
        阶段一:处理并行广播消息  
        阶段二:处理串行广播消息 
        阶段三:设定定时消息 
        阶段四:向“动态”接收器派发广播消息  阶段五:向“静态”接收器派发广播消息  
        // 静态接收器的类型都是 ResolveInfo 
        ResolveInfo info = (ResolveInfo)nextReceiver;  
        // ① 权限检查 
        ComponentName component = new ComponentName(info.activityInfo.applicationInfo.packageName, info.activityInfo.name); 
        ... ... 
        String targetProcess = info.activityInfo.processName; 
        // ② 获取接收器所在的进程 
        ProcessRecord app = mService.getProcessRecordLocked(targetProcess, info.activityInfo.applicationInfo.uid, false); 
        ... ... 
        // ③ 进程已经启动 
        // Is this receiver's application already running? 
        if (app != null && app.thread != null && !app.killed) { 
            try { 
                app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, mService.mProcessStats); 
                processCurBroadcastLocked(r, app); 
                return; 
            } catch (RemoteException e) { 
            ... ... 
            } 
        }  
        
        // ④ 进程还未启动 
        if ((r.curApp=mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast", r.curComponent, (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false)) == null) {     
            logBroadcastReceiverDiscardLocked(r); 
            finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false); 
            scheduleBroadcastsLocked(); 
            r.state = BroadcastRecord.IDLE; 
            return; 
        } 
        // ⑤ 进程启动失败 
        mPendingBroadcast = r; 
        mPendingBroadcastRecvIndex = recIdx; 
    } 
} 

1、权限检查

“静态”接收器是 ResolveInfo ,需要通过 PackageManager 获取包信息,进行权限检查。

2、获取接收器所在的进程

经过一系列复杂的权限检查后,终于可以向目标接收器派发了。通过 AMS.getProcessRecordLocked() 获取广播接收器的进程信息。

3、进程已经启动

如果 app.thread != null ,则进程已经启动,就可以调用 BroadcastQueue.processCurBroadcastLocked() 进行接下来的派发处理了。

4、进程还未启动

如果进程还没有启动,则需要通过 AMS.startProcessLocked() 来启动进程,当前消息并未派发,调用 BroadcastQueue.scheduleBroadcastsLocked() 进入下一次的调度。

5、进程启动失败

如果进程启动失败了,则当前消息记录成 mPendingBroadcast ,即阻塞的广播消息,等待下一次调度时处理。

最终有两个方法将广播消息派发出去: BroadcastQueue.deliverToRegisteredReceiverLocked() 和 BroadcastQueue.processCurBroadcastLocked()。

抛开这两个函数的逻辑,试想要将广播消息从 AMS 所在的 system_server 进程传递到应用程序的进程,该怎么实现?

自然需要用到跨进程调用,Android中最常规的手段就是 Binder机制

对于应用程序已经启动(app.thread != null)的情况,会通过 IApplicationThread 发起跨进程调用:

// Is this receiver's application already running? 
if (app != null && app.thread != null && !app.killed) { 
    try { 
        app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, mService.mProcessStats); 
        processCurBroadcastLocked(r, app);
private final void processCurBroadcastLocked(BroadcastRecord r, ProcessRecord app) throws RemoteException { 
    ... ... 
    boolean started = false; 
    try { 
        mService.notifyPackageUse(r.intent.getComponent().getPackageName(), PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER); 
        // 通过 IApplicationThread 发起跨进程调用 
        app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId, app.repProcState); 
        started = true; 
    } finally { 
        ... ... 
    } 
}
ActivityThread.ApplicationThread.scheduleReceiver()
└── ActivityThread.handleReceiver()
    └── BroadcastReceiver.onReceive()

对于应用程序还未启动的情况,会调用 IIntentReceiver 发起跨进程调用,应用进程的实现在LoadedApk.ReceiverDispatcher.IntentReceiver中,调用关系如下:

LoadedApk.ReceiverDispatcher.IntentReceiver.performReceive() 
└── LoadedApk.ReceiverDispatcher.performReceiver() 
└── LoadedApk.ReceiverDispatcher.Args.run() 
└── BroadcastReceiver.onReceive()

最终都会调用到 BroadcastReceiver.onReceive() ,在应用进程执行接收广播消息的具体动作。

对于“串行广播消息”而言,执行完了以后,还需要通知 system_server 进程,才能继续将广播消息派发到下一个接收器,这又需要跨进程调用了。

应用进程在处理完广播消息后,即在 BroadcastReceiver.onReceive() 执行完毕后,会调用 BroadcastReceiver.PendingResult.finish() , 接下来的调用关系如下:

BroadcastReceiver.PendingResult.finish() 
└── BroadcastReceiver.PendingResult.sendFinished() 
└── IActivityManager.finishReceiver() 
└── ActivityManagerService.finishReceiver() 
└── BroadcastQueue.processNextBroadcat()

通过IActivityManager发起了一个从应用进程到 system_server 进程的调用,最终在AMS线程中,又走到了 BroadcastQueue.processNextBroadcat(), 开始下一轮的调度。

两种 ANR 机制最终都会调用 BroadcastQueue.broadcastTimeoutLocked() 方法, 第一种 ANR 监测生效时,会将 fromMsg 设置为 false ;第二种 ANR 监测生效时,会将 fromMsg 参数为 true 时,表示当前正在响应 BROADCAST_TIMEOUT_MSG 消息。

final void broadcastTimeoutLocked(boolean fromMsg) {  
    // ① 设置mPendingBroadcastTimeoutMessage 
    if (fromMsg) { 
        mPendingBroadcastTimeoutMessage = false; 
        mService.mAnrManager.removeBroadcastMonitorMessage(); 
    } 
    ... ... 
    long now = SystemClock.uptimeMillis(); 
    // ② 判断第二种ANR机制是否超时 
    BroadcastRecord r = mOrderedBroadcasts.get(0); 
    if (fromMsg) { 
        ... ... 
        long timeoutTime = r.receiverTime + mTimeoutPeriod; 
        if (timeoutTime > now) { 
            ... ... 
            setBroadcastTimeoutLocked(timeoutTime); 
            return; 
        } 
    } 
    ... ...  
    // ③ 已经超时,则结束对当前接收器,开始新一轮调度 finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false); 
    scheduleBroadcastsLocked();  
    // ④ 抛出绘制ANR对话框的消息 
    if (anrMessage != null) { 
    // Post the ANR to the handler since we do not want to process ANRs while 
    // potentially holding our lock. 
    mHandler.post(new AppNotResponding(app, anrMessage)); 
    } 
}

1、设置mPendingBroadcastTimeoutMessage

mPendingBroadcastTimeoutMessage 标识是否存在未处理的 BROADCAST_TIMEOUT_MSG 消息,将其设置成false,允许继续抛出BROADCAST_TIMEOUT_MSG消息。

2、判断第二种ANR机制是否超时

每次将广播派发到接收器,都会将 r.receiverTime 更新,如果判断当前还未超时,则又抛出一个 BROADCAST_TIMEOUT_MSG 消息。 正常情况下,所有接收器处理完毕后,才会清除 BROADCAST_TIMEOUT_MSG ;否则,每进行一次广播消息的调度,都会抛出 BROADCAST_TIMEOUT_MSG 消息。

3、已经超时,则结束对当前接收器,开始新一轮调度

判断已经超时了,说明当前的广播接收器还未处理完毕,则结束掉当前的接收器,开始新一轮广播调度。

4、抛出绘制ANR对话框的消息

最终,发出绘制 ANR 对话框的消息。

AMS 维护着广播队列 BroadcastQueue ,AMS 线程不断从队列中取出消息进行调度,完成广播消息的派发。在派发“串行广播消息”时,会抛出一个定时消息 BROADCAST_TIMEOUT_MSG ,在广播接收器处理完毕后,AMS 会将定时消息清除。如果 BROADCAST_TIMEOUT_MSG 得到了响应,就会判断是否广播消息处理超时,最终通知ANR的发生。

Input 超时处理

应用程序可以接收输入事件(按键、触屏、轨迹球等),当 5秒 内没有处理完毕时,则会引发 ANR。

输入事件经历了一些什么工序才能被派发到应用的界面?

如何检测输入事件处理超时?

输入事件派发工序

输入事件最开始由硬件设备(如按键或触摸屏幕)发起,Android 有一套输入子系统来发现各种输入事件,这些事件最终都会被 InputDispatcher 分发到各个需要接收事件的窗口。那么,窗口如何告之 InputDispatcher 自己需要处理输入事件呢?Android 通过 InputChannel 连接 InputDispatcher 和窗口,InputChannel 其实是封装后的 Linux管道(Pipe)。每一个窗口都会有一个独立的 InputChannel ,窗口需要将这个 InputChannel 注册到 InputDispatcher 中:

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { 
    ... 
    sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor); 
    int fd = inputChannel->getFd(); 
    mConnectionsByFd.add(fd, connection); 
    ... 
    mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); 
    ... 
    mLooper->wake(); 
    return OK; 
}

对于 InputDispatcher 而言,每注册一个 InputChannel 都被视为一个 Connection ,通过文件描述符来区别。InputDispatcher 是一个消息处理循环,当有新的 Connection 时,就需要唤醒消息循环队列进行处理。

输入事件的类型有很多,按键、轨迹球、触屏等,Android 对这些事件进行了分类,处理这些事件的窗口也被赋予了一个类型(targetType):Foucused 或 Touched。

如果当前输入事件是按键类型,则寻找 Focused 类型的窗口。如果当前输入事件类型是触摸类型,则寻找 Touched 类型的窗口。

InputDispatcher 需要经过以下复杂的调用关系,才能把一个输入事件派发出去(调用关系以按键事件为例,触屏事件的调用关系类似):

InputDispatcherThread::threadLoop() 
└── InputDispatcher::dispatchOnce() 
└── InputDispatcher::dispatchOnceInnerLocked() 
└── InputDispatcher::dispatchKeyLocked() 
└── InputDispatcher::dispatchEventLocked() 
└── InputDispatcher::prepareDispatchCycleLocked() 
└── InputDispatcher::enqueueDispatchEntriesLocked() 
└── InputDispatcher::startDispatchCycleLocked() 
└── InputPublisher::publishKeyEvent()

具体每个函数的实现逻辑此处不表。我们提炼出几个关键点:

  InputDispatcherThread 是一个线程,它处理一次消息的派发。

  输入事件作为一个消息,需要排队等待派发,每一个 Connection 都维护两个队列:
        · outboundQueue: 等待发送给窗口的事件。每一个新消息到来,都会先进入到此队列。
        · waitQueue: 已经发送给窗口的事件。

  publishKeyEvent 完成后,表示事件已经派发了,就将事件从 outboundQueue 挪到了 waitQueue。

事件经过这么一轮处理,就算是从 InputDispatcher 派发出去了,但事件是不是被窗口收到了,还需要等待接收方的 “finished” 通知。在向 InputDispatcher 注册 InputChannel 的时候,同时会注册一个回调函数 handleReceiveCallback():

int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { 
    ... 
    for (;;) { 
        ... 
        status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled); 
        if (status) { break; } 
        d->finishDispatchCycleLocked(currentTime, connection, seq, handled); ... 
    } 
    ... 
    d->unregisterInputChannelLocked(connection->inputChannel, notify); 
}

当收到的 status 为 OK 时,会调用 finishDispatchCycleLocked() 来完成一个消息的处理:

InputDispatcher::finishDispatchCycleLocked() 
└── InputDispatcher::onDispatchCycleFinishedLocked() 
└── InputDispatcher::doDispatchCycleFinishedLockedInterruptible() 
└── InputDispatcher::startDispatchCycleLocked()

调用到 doDispatchCycleFinishedLockedInterruptible() 方法时,会将已经成功派发的消息从 waitQueue 中移除, 进一步调用会 startDispatchCycleLocked 开始派发新的事件。

至此,我们回答了第一个问题:

一个正常的输入事件会经过从 outboundQueue 挪到 waitQueue 的过程,表示消息已经派发出去;再经过从 waitQueue 中移除的过程,表示消息已经被窗口接收。InputDispatcher 作为中枢,不停地在递送着输入事件,当一个事件无法得到处理的时候,InputDispatcher 不能就此死掉啊,否则系统也太容易崩溃了。InputDispatcher 的策略是放弃掉处理不过来的事件,并发出通知(这个通知机制就是ANR),继续进行下一轮消息的处理。

理解输入事件分发模型,我们可以举一个生活中的例子:

每一个输入事件可以比做一个快递,InputDispatcher 就像一个快递中转站,窗口就像是收件人,InputChannel 就像是快递员。 所有快递都会经过中转站中处理,中转站需要知道每一个快递的收件人是谁,通过快递员将快递发送到具体的收件人。 这其中有很多场景导致快递不能及时送到:譬如联系不到收件人;快递很多,快递员会忙不过来;快递员受伤休假了等等… 这时候快递员就需要告知中转站:有快递无法及时送到了。中转站在收到快递员的通知后,一边继续派发其他快递,一边报告上级。

检测输入事件处理超时

在派发事件时,dispatchKeyLocked() 和 dispatchMotionLocked(),需要找到当前的焦点窗口,焦点窗口才是最终接收事件的地方,找窗口的过程就会判断是否已经发生了ANR:

InputDispatcher::findFocusedWindowTargetsLocked() 
InputDispatcher::findTouchedWindowTargetsLocked() 
└── InputDispatcher::handleTargetsNotReadyLocked() 
└── InputDispatcher::onANRLocked() 
└── InputDispatcher::doNotifyANRLockedInterruptible() 
└── NativeInputManager::notifyANR()

(1)首先,会调用 findFocusedWindowTargetsLocked() 或 findTouchedWindowTargetsLocked() 寻找接收输入事件的窗口。

在找到窗口以后,会调用 checkWindowReadyForMoreInputLocked() 检查窗口是否有能力再接收新的输入事件,会有一系列的场景阻碍事件的继续派发:

场景1: 窗口处于paused状态,不能处理输入事件

“Waiting because the [targetType] window is paused.”

场景2: 窗口还未向InputDispatcher注册,无法将事件派发到窗口

“Waiting because the [targetType] window’s input channel is not registered with the input dispatcher. The window may be in the process of being removed.”

场景3: 窗口和InputDispatcher的连接已经中断,即InputChannel不能正常工作

“Waiting because the [targetType] window’s input connection is [status]. The window may be in the process of being removed.”

场景4: InputChannel已经饱和,不能再处理新的事件

“Waiting because the [targetType] window’s input channel is full. Outbound queue length: %d. Wait queue length: %d.”

场景5: 对于按键类型(KeyEvent)的输入事件,需要等待上一个事件处理完毕

“Waiting to send key event because the [targetType] window has not finished processing all of the input events that were previously delivered to it. Outbound queue length: %d. Wait queue length: %d.”

场景6: 对于触摸类型(TouchEvent)的输入事件,可以立即派发到当前的窗口,因为TouchEvent都是发生在用户当前可见的窗口。但有一种情况, 如果当前应用由于队列有太多的输入事件等待派发,导致发生了ANR,那TouchEvent事件就需要排队等待派发。

“Waiting to send non-key event because the %s window has not finished processing certain input events that were delivered to it over %0.1fms ago. Wait queue length: %d. Wait queue head age: %0.1fms.”

(2)然后,上述有任何一个场景发生了,则输入事件需要继续等待,紧接着就会调用 handleTargetsNotReadyLocked() 来判断是不是已经的等待超时了:

int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { 
    ... 
    if (currentTime >= mInputTargetWaitTimeoutTime) { 
    onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime, mInputTargetWaitStartTime, reason); 
    *nextWakeupTime = LONG_LONG_MIN; 
    return INPUT_EVENT_INJECTION_PENDING; 
    } 
    ... 
}

最后,如果当前事件派发已经超时,则说明已经检测到了 ANR ,调用 onANRLocked() 方法,然后将 nextWakeupTime 设置为最小值,马上开始下一轮调度。在 onANRLocked() 方法中, 会保存 ANR 的一些状态信息,调用 doNotifyANRLockedInterruptible() ,进一步会调用到 JNI 层的 NativeInputManager::notifyANR() 方法,它的主要功能就是衔接 Native 层和 Java 层,直接调用 Java 层的 InputManagerService.notifyANR() 方法。

至此,ANR的处理逻辑转交到了Java层。底层(Native)发现一旦有输入事件派发超时,就会通知上层(Java),上层收到ANR通知后,决定是否终止当前输入事件的派发。

发生ANR时,Java层最开始的入口是 InputManagerService.notifyANR() ,它是直接被 Native 层调用的。我们先把 ANR 的Java层调用关系列出来:

InputManagerService.notifyANR() 
└── InputMonitor.notifyANR() 
├── IApplicationToken.keyDispatchingTimedOut() 
│ └── ActivityRecord.keyDispatchingTimedOut() 
│ └── AMS.inputDispatchingTimedOut() 
│ └── AMS.appNotResponding() 
└── AMS.inputDispatchingTimedOut() 
└── AMS.appNotResponding()

InputManagerService.notifyANR() 只是为 Native层 定义了一个接口,它直接调用 InputMonitor.notifyANR()。如果该方法的返回值等于0,则放弃本次输入事件;如果大于0,则表示需要继续等待的时间。

public long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason) { 
    ... 
    if (appWindowToken != null && appWindowToken.appToken != null) { 
        // appToken实际上就是当前的ActivityRecord。 
        // 如果发生ANR的Activity还存在,则直接通过ActivityRecord通知事件派发超时 
        boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason); 
        if (! abort) { 
            return appWindowToken.inputDispatchingTimeoutNanos; 
        } 
    } else if (windowState != null) { 
        // 如果发生ANR的Activity已经销毁了,则通过AMS通知事件派发超时 
        long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut( windowState.mSession.mPid, aboveSystem, reason); 
        if (timeout >= 0) { return timeout; } 
    } 
    return 0; 
    // abort dispatching 
}

上述方法中有两种不同的调用方式,但最终都会交由 AMS.inputDispatchingTimedOut() 处理。AMS 有重载的 inputDispatchingTimedOut() 方法,他们的参数不一样。ActivityRecord 调用时,可以传入的信息更多一点(当前发生ANR的界面是哪一个)。

@Override public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { 
    // 1. 根据进程号获取到ProcessRecord 
    proc = mPidsSelfLocked.get(pid); 
    ... 
    // 2. 获取超时时间 
    // 测试环境下的超时时间是INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT(60秒), 
    // 正常环境下的超时时间是KEY_DISPATCHING_TIMEOUT(5秒) 
    timeout = getInputDispatchingTimeoutLocked(proc); 
    // 调用重载的函数,如果返回True,则表示需要中断当前的事件派发; 
    if (!inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) { return -1; } 
    // 3. 返回继续等待的时间,这个值会传递到Native层 
    return timeout; 
} 

public boolean inputDispatchingTimedOut(final ProcessRecord proc, final ActivityRecord activity, final ActivityRecord parent, final boolean aboveSystem, String reason) { 
    ... 
    // 1. 发生ANR进程正处于调试状态,不需要中断事件 
    if (proc.debugging) { return false; } 
    // 2. 当前正在做dexopt操作,这会比较耗时,不需要中断 
    if (mDidDexOpt) { 
        // Give more time since we were dexopting. 
        mDidDexOpt = false; return false; 
    } 
    // 3. 发生ANR的进程是测试进程,需要中断,但不在UI界面显示ANR信息判断 
    if (proc.instrumentationClass != null) { ...     
        finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info); return true; 
    } 
    // 4. 通知UI界面显示ANR信息 
    mHandler.post(new Runnable() { 
        @Override public void run() { 
            appNotResponding(proc, activity, parent, aboveSystem, annotation); 
        } }); 
        ... 
        return true; 
}

在 InputDispatcher 派发输入事件时,会寻找接收事件的窗口,如果无法正常派发,则可能会导致当前需要派发的事件超时(默认是5秒)。Native层发现超时了,会通知Java层,Java层经过一些处理后,会反馈给Native层,是继续等待还是丢弃当前派发的事件。

ANR 信息收集过程

// frameworks/base/services/core/java/com/android/server/am/AppErrors.java

class AppErrors {

    final void appNotResponding(ProcessRecord app, ActivityRecord activity,
            ActivityRecord parent, boolean aboveSystem, final String annotation) {
        ... ...

        long anrTime = SystemClock.uptimeMillis();
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
            mService.updateCpuStatsNow();   // 更新 cpu 统计信息
        }

        boolean showBackground = Settings.Secure.
                getInt(mContext.getContentResolver(),
                           Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;

        boolean isSilentANR;

        synchronized (mService) {
            if (mService.mShuttingDown) {
                return;
            } else if (app.notResponding) {
                return;
            } else if (app.crashing) {
                return;
            } else if (app.killedByAm) {
                return;
            } else if (app.killed) {
                return;
            }

            // In case we come through here for the same app before completing
            // this one, mark as anring now so we will bail out.
            app.notResponding = true;

            // 记录 ANR 到 EventLog
            EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
                    app.processName, app.info.flags, annotation);

            // 将当前进程添加到 firstPids
            firstPids.add(app.pid);

            // Don't dump other PIDs if it's a background ANR
            isSilentANR = !showBackground 
                                  && !isInterestingForBackgroundTraces(app);
            if (!isSilentANR) {
                int parentPid = app.pid;
                if (parent != null && parent.app != null && parent.app.pid > 0) {
                    parentPid = parent.app.pid;
                }
                if (parentPid != app.pid) firstPids.add(parentPid);

                // 将 system_server 进程添加到 firstPids
                if (MY_PID != app.pid 
                                && MY_PID != parentPid) firstPids.add(MY_PID);

                for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
                    ProcessRecord r = mService.mLruProcesses.get(i);
                    if (r != null && r.thread != null) {
                        int pid = r.pid;
                        if (pid > 0 && pid != app.pid 
                                       && pid != parentPid && pid != MY_PID) {
                            if (r.persistent) {
                                // 将 persistent 进程添加到 firstPids
                                firstPids.add(pid);
                            } else if (r.treatLikeActivity) {
                                firstPids.add(pid);
                            } else {
                                // 其他进程添加到 lastPids
                                lastPids.put(pid, Boolean.TRUE);
                            }
                        }
                    }
                }
            }
        }

        // 记录 ANR 输出到 main log
        StringBuilder info = new StringBuilder();
        info.setLength(0);
        info.append("ANR in ").append(app.processName);
        if (activity != null && activity.shortComponentName != null) {
            info.append(" (").append(activity.shortComponentName).append(")");
        }
        info.append("\n");
        info.append("PID: ").append(app.pid).append("\n");
        if (annotation != null) {
            info.append("Reason: ").append(annotation).append("\n");
        }
        if (parent != null && parent != activity) {
            info.append("Parent: ").append(parent.shortComponentName).append("\n");
        }

        // 创建 CPU tracker 对象 
        ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);

        ... ...

        // 输出 traces 信息
        File tracesFile = ActivityManagerService.dumpStackTraces(
                true, firstPids,
                (isSilentANR) ? null : processCpuTracker,
                (isSilentANR) ? null : lastPids,
                nativePids);

        String cpuInfo = null;
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
            mService.updateCpuStatsNow();
            synchronized (mService.mProcessCpuTracker) {
                cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
            }
            // 记录当前 CPU 负载情况
            info.append(processCpuTracker.printCurrentLoad());
            info.append(cpuInfo);
        }

        // 记录从 anr 时间开始的 Cpu 使用情况
        info.append(processCpuTracker.printCurrentState(anrTime));

        // 输出当前 ANR 的 reason,以及 CPU 使用率、负载信息
        Slog.e(TAG, info.toString());
        if (tracesFile == null) {
            Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
        }
        ... ...
                        
        // 将 traces 文件和 CPU 使用率信息保存到 dropbox,即 data/system/dropbox 目录
        mService.addErrorToDropBox("anr", app, app.processName,
                          activity, parent, annotation, cpuInfo, tracesFile, null);
        ... ...

        synchronized (mService) {
            mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);

            // 后台 ANR 的情况, 直接杀掉
            if (isSilentANR) {
                app.kill("bg anr", true);
                return;
            }

            // 设置 app 的 ANR 状态,病查询错误报告 receiver
            makeAppNotRespondingLocked(app,
                    activity != null ? activity.shortComponentName : null,
                    annotation != null ? "ANR " + annotation : "ANR",
                    info.toString());

            // 弹出 ANR 对话框
            Message msg = Message.obtain();
            msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
            msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);

            // 向 ui 线程发送,内容为 SHOW_NOT_RESPONDING_MSG 的消息
            mService.mUiHandler.sendMessage(msg);
        }
    }
    
}

当发生 ANR 时, 会按顺序依次执行:

1、输出 ANR Reason 信息到 EventLog,也就是说 ANR 触发的时间点最接近的就是 EventLog 中输出的 am_anr 信息;

2、收集并输出重要进程列表中的各个线程的 traces 信息,该方法较耗时;

3、输出当前各个进程的 CPU 使用情况以及 CPU 负载情况;

4、将 traces 文件和 CPU 使用情况信息保存到 dropbox,即 data/system/dropbox 目录;

5、根据进程类型,来决定直接后台杀掉,还是弹框告知用户。

ANR输出重要进程的traces信息,这些进程包含:

1、firstPids 队列:第一个是 ANR 进程,第二个是 system_server,剩余是所有 persistent 进程;

2、Native 队列:是指 /system/bin/ 目录的 mediaserver、sdcard 以及 surfaceflinger 进程;

3、lastPids 队列: 是指 mLruProcesses 中的不属于 firstPids 的所有进程。

dump 出 trace 信息的流程:

// ActivityManagerService.java

    public static File dumpStackTraces(boolean clearTraces, ... ,nativePids) {
        ... ...

        if (tracesDirProp.isEmpty()) {
            // 默认为 data/anr/traces.txt
            String globalTracesPath = 
                          SystemProperties.get("dalvik.vm.stack-trace-file", null);

            tracesFile = new File(globalTracesPath);
            try {
                if (clearTraces && tracesFile.exists()) {
                    tracesFile.delete();      // 删除已存在的 traces 文件
                }

                // 这里会保证 data/anr/traces.txt 文件内容是全新的方式,而非追加
                tracesFile.createNewFile();   // 创建 traces 文件
                FileUtils.setPermissions(globalTracesPath, 0666, -1, -1);
            } catch (IOException e) {
                Slog.w(TAG, "Unable to prepare ANR traces file: " + tracesFile, e);
                return null;
            }
        } else {
        }

        // 输出 trace 内容
        dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids,
                                         extraPids, useTombstonedForJavaTraces);
        return tracesFile;
    }
// ActivityManagerService.java

    private static void dumpStackTraces(String tracesFile, ...) {

        final DumpStackFileObserver observer;
        if (useTombstonedForJavaTraces) {
            observer = null;
        } else {
            observer = new DumpStackFileObserver(tracesFile);
        }

        // We must complete all stack dumps within 20 seconds.
        long remainingTime = 20 * 1000;
        try {
            if (observer != null) {
                observer.startWatching();
            }

            // 首先,获取 firstPids 进程的 stacks
            if (firstPids != null) {
                int num = firstPids.size();
                for (int i = 0; i < num; i++) {
                    final long timeTaken;
                    if (useTombstonedForJavaTraces) {
                        timeTaken = dumpJavaTracesTombstoned(firstPids.get(i),
                                                   tracesFile, remainingTime);
                    } else {
                        timeTaken = observer.dumpWithTimeout(firstPids.get(i),
                                                               remainingTime);
                    }
                    ... ...    
                }
            }

            // 下一步,获取 native 进程的 stacks
            if (nativePids != null) {
                for (int pid : nativePids) {
                    ... ...
                    
                    // 输出 native 进程的 trace
                    Debug.dumpNativeBacktraceToFileTimeout(
                            pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
                            
                    final long timeTaken = SystemClock.elapsedRealtime() - start;
                    ... ...
                }
            }

            // Lastly, dump stacks for all extra PIDs from the CPU tracker.
            if (extraPids != null) {
                ... ...
                }
            }
        } finally {
            if (observer != null) {
                observer.stopWatching();
            }
        }
    }

触发 ANR 时系统会输出关键信息:

1、将 am_anr 信息,输出到 EventLog;

2、获取重要进程 trace 信息,保存到 /data/anr/traces.txt;

3、ANR reason 以及 CPU 使用情况信息,输出到 main log;

4、再将 CPU使用情况 和进程 trace 文件信息,再保存到 /data/system/dropbox。

小结

ANR监测机制包含三种:

  Service ANR,前台进程中Service生命周期不能超过 20秒,后台进程中Service的生命周期不能超过 200秒。 在启动Service时,抛出定时消息 SERVICE_TIMEOUT_MSG 或 SERVICE_BACKGOURND_TIMEOUT_MSG,如果定时消息响应了,则说明发生了ANR。

  Broadcast ANR,前台的“串行广播消息”必须在 10秒 内处理完毕,后台的“串行广播消息”必须在 60秒 处理完毕, 每派发串行广播消息到一个接收器时,都会抛出一个定时消息 BROADCAST_TIMEOUT_MSG,如果定时消息响应,则判断是否广播消息处理超时,超时就说明发生了ANR。

  Input ANR,输入事件必须在 5秒 内处理完毕。在派发一个输入事件时,会判断当前输入事件是否需要等待,如果需要等待,则判断是否等待已经超时,超时就说明发生了ANR。

ANR监测机制实际上是对应用程序主线程的要求,要求主线成必须在限定的时间内,完成对几种操作的响应;否则,就可以认为应用程序主线程失去响应能力。

从ANR的三种监测机制中,我们看到不同超时机制的设计:

  Service 和 Broadcast 都是由 AMS 调度,利用 Handler 和 Looper,设计了一个 TIMEOUT 消息交由 AMS 线程来处理,整个超时机制的实现都是在Java层;
  
  InputEvent 由 InputDispatcher 调度,待处理的输入事件都会进入队列中等待,设计了一个等待超时的判断,超时机制的实现在Native层。