ANR类型

类型 超时时刻 是否有弹窗提示
Activity Timeout 10s 有提示
BroadcastReceiver Timeout 10s, 60s 无感知场景不会提示,如在后台
Service Timeout 20s, 200s 无感知场景不会提示
ContentProvider Timeout 10s 无感知场景不会提示
InputDispatching Timeout 5s 有提示

之前的文章,咱们分析了Input ANR是怎么产生的,详细能够参阅:ANR怎么产生之InputDispatching Timeout

今日这篇文章,咱们来讲讲BroadcastReceiverService Timeout ANR分别是怎样产生的。

系统检测ANR的核心原理如下:

  • 在流程开始时,发动一个计时器,比方往MessageQueue加入一个守时履行的Message
  • 假如流程在规则的时刻内完毕,则封闭计时器,比方移除这个Message
  • 假如流程未准时完毕,履行该Message,触发ANR

Service Timeout

1. 发动Service时,设置计时器

Service发动的流程,省掉zygote fork进程的流程,最终会在system_server进程中调用到ActiveServices中的realStartServiceLocked办法。

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
    // 发送delay音讯(SERVICE_TIMEOUT_MSG)
    bumpServiceExecutingLocked(r, execInFg, "create");
    try {
        // 跨进程调用,调用Service所在进程的ActivityThread中的办法
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
    } catch (DeadObjectException e) {
    } 
}

在这个办法里,首要做了两件工作:

  • 发送SERVICE_TIMEOUT_MSGdelay时刻为SERVICE_TIME_OUT
  • 跨进程调用,调用Service所在进程的ActivityThread中的Service初始化办法

bumpServiceExcutingLocked便是往MesageQueue里放一个守时履行的Message

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    // 当超时后仍没有remove该SERVICE_TIMEOUT_MSG音讯,则履行service Timeout流程
    mAm.mHandler.sendMessageAtTime(msg,
        proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}

这个守时履行的Message的履行时刻是,当时时刻加上SERVICE_TIME_OUT

SERVICE_TIME_OUT有两种状况:

  • 假如是前台ServiceSERVICE_TIMEOUT = 20s
  • 假如是后台ServiceSERVICE_BACKGROUND_TIMEOUT = 200s

2. Service履行完,移除计时器

Service所在进程的ActivityThread中,履行Service的初始化办法。

private void handleCreateService(CreateServiceData data) {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        Service service = (Service) cl.loadClass(data.info.name).newInstance();
        try {
            //创建ContextImpl目标
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            //创建Application目标
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            //调用服务onCreate()办法 
            service.onCreate();
            // 告诉system_server,service发动完成
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (Exception e) {
            ...
        }
    }

从上面的办法能够看到,当Service在进程中初始化完成后,会经过跨进程调用,告诉system_server

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) {
        // 移除SERVICE_TIMEOUT_MSG
        mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
}

3. 假如未准时履行完,履行Message

final class MainHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case SERVICE_TIMEOUT_MSG: {
                mServices.serviceTimeout((ProcessRecord)msg.obj);
            } break;
        }
    }
}

AMSMainHandler中,会处理SERVICE_TIMEOUT_MSG,调用ActiveServiceserviceTimeout办法。

void serviceTimeout(ProcessRecord proc) {
    String anrMessage = null;
    synchronized(mAm) {
        if (timeout != null && mAm.mLruProcesses.contains(proc)) {
            // 拼接anr信息
            anrMessage = "executing service " + timeout.shortName;
        }
    }
    if (anrMessage != null) {
        //当存在timeout的service,履行appNotResponding
        mAm.appNotResponding(proc, null, null, false, anrMessage);
    }
}

在这个办法里,首要完成拼接ANR信息,以”executing service“开头,一起调用AMSappNotResponding处理后续的dump和弹窗逻辑。

appNotResponding的处理和收集信息流程,能够参阅这篇文章:Android产生ANR后的信息收集过程

Broadcast Receiver Timeout

播送分为有序播送和无序播送。

关于无序播送,遍历一切的播送接收者并发送,不关心接收者是否处理完播送音讯。

有序播送,需要等上一个接收者处理完之后,才干发送给后续的接收者,所以会产生ANR。

1. 发送播送时,设置计时器

发送播送的流程,最终会调用processNextBroadcastLocked来处理播送。这个办法特别长,咱们只截取其间需要的部分。

final void processNextBroadcast(boolean fromMsg) {
    synchronized(mService) {
        ...
        // 处理当时有序播送
        do {
            r = mOrderedBroadcasts.get(0);
            // 获取该播送一切的接收者
            int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
            if (mService.mProcessesReady && r.dispatchTime > 0) {
                long now = SystemClock.uptimeMillis();
                if ((numReceivers > 0) &&
                        (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                    //当播送处理时刻超时,则强制完毕这条播送
                    broadcastTimeoutLocked(false);
                }
            }
            // 当播送分发完毕,调用了finishReceiverLocked后,r.receivers为null
            if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                if (r.resultTo != null) {
                    //处理播送音讯音讯
                    performReceiveLocked(r.callerApp, r.resultTo,
                        new Intent(r.intent), r.resultCode,
                        r.resultData, r.resultExtras, false, false, r.userId);
                    r.resultTo = null;
                }
                // 移除MSG
                cancelBroadcastTimeoutLocked();
            }
        } while (r == null);
        r.receiverTime = SystemClock.uptimeMillis();
        if (!mPendingBroadcastTimeoutMessage) {
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            // 设置播送的超时MSG
            setBroadcastTimeoutLocked(timeoutTime);
        }
        // 处理动态播送
        if (nextReceiver instanceof BroadcastFilter) {
            deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
            return;
        }
        // 省掉处理静态播送的流程
    }
}

有两种状况会引发BroadcastReceiver的超时:

  • 某个播送的总处理时长 > 2 * receiver总个数 + mTimeoutPeriod
  • 某个receiver的处理商场超越mTimeoutPeriod

setBroadcastTimeoutLocked的原理便是往Handler里发送一个BROADCAST_TIMEOUT_MSG。

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

2. 移除计时器

cancelBroadcastTimeoutLocked的调用时机是,播送完毕后,调用AMSfinishReceiver办法,然后调用nextReceiver,最终调用到processNextBroadcastLocked办法中。

因为这个时候,r.receivers null,所以会履行cancelBroadcastTimeoutLocked办法。

总结

Service Timeout ANR产生原理:

  • 发动Service时,发送一个延时履行的SERVICE_TIMEOUT_MSG
  • 假如Service在规则的时刻内发动,则移除SERVICE_TIMEOUT_MSG
  • 假如Service没有在规则的时刻内发动,则履行SERVICE_TIMEOUT_MSG

留意:以上判断ANR的逻辑,都在system_server进程中履行。