ANR的原理分析和简单总结

前言

ANR(Application Not Responding),应用无响应,这个可能每个人都碰到过。

该篇主要简单总结下,ANR的几种常见类型(输入事件、广播接收器、Service、ContentProvider),以及ANR一般如何产生的及如何避免。

最后重点是通过源码 了解这几种类型 是如何产生ANR、超时时间是怎么来的、ANR后如何处理的等。

关于 ANR发生后如何分析和处理 这个不在该篇总结,会另起一篇 尽可能比较详细的说明。

ANR简述

几种ANR类型简单总结

几种ANR简单用表格列出下,下面几种情况都会造成ANR:

ANR类型 超时时间 报错信息
输入事件(按键、触摸等) 5s Input event dispatching timed out
广播BroadcastReceiver 前台10s,后台/offload 60s Receiver during timeout of
Service服务 Foreground 10s,普通 20s,后台 200s Timeout executing service
ContentProvider 10s timeout publishing content providers

如上述表格所述,列出了4种ANR的 超时时间以及报错信息。

在后面,有这 四种会引起ANR类型 源码分析(该篇重点),能够看到超时时间是如何来的,报错如何产生的,产出ANR后是如何处理的(除ContentProvider外,其他都会调用ProcessRecord.appNotResponding())。

注:ContentProvider算不算ANR类型,个人理解 算是,但与其他几种比有点区别。


ANR的产生

ANR的产生就是超时。

长时间无法响应用户输入 或 无效占用资源,会使用户感觉卡顿的感觉。一般这种情况,会弹出对话框提示用户,可以用来选择关闭应用。

这个超时,由 AMS和WMS检测(后面的源码分析可以看到,官网 上也有相关说明:AMS和WMS监控应用的响应性),未在规定时间完成特定任务(如5s未响应输入时间、10s内广播接收器未处理完毕),即会引起ANR。

这个超时,一般由Handler机制 的 延迟发送消息完成。若超时 则发出消息 产生ANR 告知系统及用户,若在时间内完成,则取消消息队列中的延迟消息。

为什么会导致超时,或者导致ANR?

UI线程(主线程) 阻塞:

如在主线程存取较多数据(I/O阻塞),网络阻塞等。也可能是资源 不足造成,如CPU负荷、内存或存储不足等导致I/O阻塞。

死锁和等待:

多线程的死锁和主线程的等待。

ANR的避免

有上面的可能可能原因,可以针对原因进行一些避免。

避免主线程阻塞:

UI线程尽量只做跟UI相关的工作,使用子线程(工作线程) 处理耗时操作或阻塞任务(如数据库、网络、I/O操作);

尽量用Handler来处理UI线程和其他线程之间的交互;

各大组件生命周期中也应避免耗时操作,如Activity的onCreate()、BroadcastReciever的onRecieve()等。广播如有耗时操作,建议放到IntentService中执行。

注: IntentService继承了Service,运行在后台线程中,处理异步需求,工作执行完毕后自动销毁。

但Android版本8.0开始限制后台,API level 30被弃用。8.0及更高版本 建议使用WorkManager 或 JobIntentService替代。

避免CPU负荷或I/O阻塞:

文件或数据操作放到子线程,异步方式完成。

ANR发生后的信息收集

一般ANR发生后 系统会调用appNotResponding()收集信息。

ANR发生后,CPU的负载情况、IOWait、traces文件(/data/anr/traces.txt)都会保存下来,导出log和traces文件进行进一步分析。

不同soc的 log会有不同,注意获取完整的log和traces文件。(如MTK平台,会生成aee_exp文件夹,其所有log也会保存在mtklog文件夹,Q版本之后mtklog变为了debuglogger)

至于 发生ANR错误 如何进行分析并解决,该篇不赘述,计划另起一篇比较详细的总结。

ANR源码分析

下面源码不复杂,主要简单了解下 ANR是如何产生 的。从这个过程,能够看到超时时间是如何来的,报错如何产生的,产出ANR后是如何处理的。

源码也是基于Android10的。

输入事件

Android10_原理机制系列_事件传递机制 中,个人感觉 对Android的事件传递过程总结的应该还比较清晰。不过事件传递在Native层过程 很复杂,考虑的状况多,那篇也只看的传递过程的主线。

记得在事件传递机制这篇中,也mark了个ANR的相关点(resetANRTimeoutsLocked()),当时也没细看。这里简单看下输入事件的ANR过程。直接列出 相关源码:

//InputDispatcher.cpp
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
        const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
        bool* outConflictingPointerActions) {
    injectionResult = handleTargetsNotReadyLocked(currentTime, entry, ...);        
}

int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, ...) {
    if(...) {
        mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
    } else {
        mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
    }
    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);
        return INPUT_EVENT_INJECTION_PENDING;
    } 
}

void InputDispatcher::onANRLocked(
        nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
        const sp<InputWindowHandle>& windowHandle,
        nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doNotifyANRLockedInterruptible);
}

void InputDispatcher::doNotifyANRLockedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();
    nsecs_t newTimeout = mPolicy->notifyANR(
            commandEntry->inputApplicationHandle,
            commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr,
            commandEntry->reason);

    mLock.lock();
    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
            commandEntry->inputChannel);
}

接着直接看到mPolicy->notifyANR(),在 Android10_原理机制系列_事件传递机制 中,mPolicy是什么,如何回到java层的都说的比较详细了,这里直接列出回到java层过程的源码:

//com_android_server_input_InputManagerService.cpp
nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
        const sp<IBinder>& token, const std::string& reason) {
    jlong newTimeout = env->CallLongMethod(mServiceObj,
                gServiceClassInfo.notifyANR, tokenObj,
                reasonObj);
    if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
        newTimeout = 0; // abort dispatch
    } else {
        assert(newTimeout >= 0);
    }
    return newTimeout;
}

//InputManagerService.java
private long notifyANR(IBinder token, String reason) {
    return mWindowManagerCallbacks.notifyANR(
            token, reason);
}

回到了IMS中,接着就直接看了。

//InputManagerCallback.java
@Override
public long notifyANR(IBinder token, String reason) {
    //输入事件log打印,不同情况打印不一样。不过头部都是Input event dispatching timed out
    synchronized (mService.mGlobalLock) {
            Slog.i(TAG_WM, "Input event dispatching timed out "
                    + ...);
    }
    
    ...
    } else if (windowState != null) {
        // Notify the activity manager about the timeout and let it decide whether
        // to abort dispatching or keep waiting.
        long timeout = mService.mAmInternal.inputDispatchingTimedOut(
                windowState.mSession.mPid, aboveSystem, reason);
        if (timeout >= 0) {
            // The activity manager declined to abort dispatching.
            // Wait a bit longer and timeout again later.
            return timeout * 1000000L; // nanoseconds
        }
    }
    return 0; // abort dispatching
}

//ActivityManagerService.java
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal {
    @Override
    public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
        synchronized (ActivityManagerService.this) {
            return ActivityManagerService.this.inputDispatchingTimedOut(
                    pid, aboveSystem, reason);
        }
    }
}

long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
    synchronized (this) {
        timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
    }
    if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {
        return -1;
    }
    return timeout;
}

boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName,
        ApplicationInfo aInfo, String parentShortComponentName,
        WindowProcessController parentProcess, boolean aboveSystem, String reason) {
    if (proc != null) {
        proc.appNotResponding(activityShortComponentName, aInfo,
                parentShortComponentName, parentProcess, aboveSystem, annotation);
    }

    return true;
}

很明显,上面可以看到ANR超时后,会调用appNotResponding()。

超时时间是多少呢?

timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;

//ActivityTaskManagerService.java
// How long we wait until we timeout on key dispatching.
public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
// How long we wait until we timeout on key dispatching during instrumentation.
static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;

//ProcessRecord.java
public long getInputDispatchingTimeout() {
    return mWindowProcessController.getInputDispatchingTimeout();
}
//WindowProcessController.java
public long getInputDispatchingTimeout() {
    synchronized (mAtm.mGlobalLock) {
        return isInstrumenting() || isUsingWrapper()
                ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS;
    }
}

很明显,正常情况下 超时事件为 5s。

BroadcastReceiver

简单的从sendBroadcast()开始看一下:

//ContextImpl.java
@Override
public void sendBroadcast(Intent intent) {
    try {
        ActivityManager.getService().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                getUserId());
    }
}

//ActivityManagerService.java
@GuardedBy("this")
final int broadcastIntentLocked(ProcessRecord callerApp,
        String callerPackage, Intent intent, String resolvedType,
        IIntentReceiver resultTo, int resultCode, String resultData,
        Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
        boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
        int realCallingPid, int userId) {
    return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo,
        resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered,
        sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
        false /* allowBackgroundActivityStarts */);
}

@GuardedBy("this")
final int broadcastIntentLocked(ProcessRecord callerApp,
        String callerPackage, Intent intent, String resolvedType,
        IIntentReceiver resultTo, int resultCode, String resultData,
        Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
        boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
        int realCallingPid, int userId, boolean allowBackgroundActivityStarts) {
    intent = new Intent(intent);
    // Figure out who all will receive this broadcast.
    List receivers = null;
    List<BroadcastFilter> registeredReceivers = null;
    ...

    if ((receivers != null && receivers.size() > 0)
            || resultTo != null) {
        BroadcastQueue queue = broadcastQueueForIntent(intent);
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                resultData, resultExtras, ordered, sticky, false, userId,
                allowBackgroundActivityStarts, timeoutExempt);
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
        final BroadcastRecord oldRecord =
                replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
        if (oldRecord != null) {
            ...
        } else {
            //加入到mOrderedBroadcasts队列中
            queue.enqueueOrderedBroadcastLocked(r);
            //这里接着往下看
            queue.scheduleBroadcastsLocked();
        }
    }
    return ActivityManager.BROADCAST_SUCCESS;
}

从sendBroadcast() 到broadcastIntentLocked() ,broadcastIntentLocked() 方法很长,对不同广播进行了不同处理,接着直接看 queue.scheduleBroadcastsLocked() 是如何处理的。

//BroadcastQueue.java
public void scheduleBroadcastsLocked() {
    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
    mBroadcastsScheduled = true;
}

private final class BroadcastHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BROADCAST_INTENT_MSG: {
                if (DEBUG_BROADCAST) Slog.v(
                        TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
                        + mQueueName + "]");
                processNextBroadcast(true);
            } break;
            case BROADCAST_TIMEOUT_MSG: {
                synchronized (mService) {
                    broadcastTimeoutLocked(true);
                }
            } break;
        }
    }
}

final void processNextBroadcast(boolean fromMsg) {
    synchronized (mService) {
        processNextBroadcastLocked(fromMsg, false);
    }
}

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    BroadcastRecord r;
    r.receiverTime = SystemClock.uptimeMillis();
    if (! mPendingBroadcastTimeoutMessage) {
        //当前时间戳+超时时间(mConstants.TIMEOUT)
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                "Submitting BROADCAST_TIMEOUT_MSG ["
                + mQueueName + "] for " + r + " at " + timeoutTime);
        setBroadcastTimeoutLocked(timeoutTime);
    }
    ...
}

final void setBroadcastTimeoutLocked(long timeoutTime) {
    if (! mPendingBroadcastTimeoutMessage) {
        //延迟发送BROADCAST_TIMEOUT_MSG消息。 回到上面看到,接收处理:broadcastTimeoutLocked(true);
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
    }
}

final void broadcastTimeoutLocked(boolean fromMsg) {
    long now = SystemClock.uptimeMillis();
    BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
    if (fromMsg) {
        //应该完成的时间
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        //未超时
        if (timeoutTime > now) {
            setBroadcastTimeoutLocked(timeoutTime);
            return;
        }
    }
    final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
    r.receiverTime = now;
    if (!debugging) {
        r.anrCount++;
    }
    ProcessRecord app = null;
    String anrMessage = null;
    Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
    ...
    if (app != null) {
        anrMessage = "Broadcast of " + r.intent.toString();
    }
    if (!debugging && 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));
    }
}

scheduleBroadcastsLocked() 通过handler最终执行了 processNextBroadcast()->processNextBroadcastLocked(),其中设置了超时时间 r.receiverTime + mConstants.TIMEOUT; 最终通过 setBroadcastTimeoutLocked()->broadcastTimeoutLocked() 处理判断是否超时,生成ANR,最终也是调用的appNotResponding()。

这个超时(mConstants.TIMEOUT) 时间到底是多少呢?

现在具体看下:

//BroadcastQueue.java
final BroadcastConstants mConstants;
BroadcastQueue(ActivityManagerService service, Handler handler,
               String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
    ...
    mConstants = constants;
}

在构造BroadcastQueue时 传入的,这个即AMS调用时 queue.scheduleBroadcastsLocked(); 中的queue

BroadcastQueue queue = broadcastQueueForIntent(intent); 下面来看下这个方法:

//ActivityManagerService.java
BroadcastQueue broadcastQueueForIntent(Intent intent) {
    if (isOnOffloadQueue(intent.getFlags())) {
        return mOffloadBroadcastQueue;
    }
    final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
    return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}

mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                                       "foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
                                       "background", backConstants, true);
mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
                                            "offload", offloadConstants, true);

看下定义,这个超时时间即在foreConstants、backConstants、offloadConstants中,在看定义:

//ActivityManagerService.java
// Broadcast policy parameters
final BroadcastConstants foreConstants = new BroadcastConstants(
    Settings.Global.BROADCAST_FG_CONSTANTS);
foreConstants.TIMEOUT = BROADCAST_FG_TIMEOUT;

final BroadcastConstants backConstants = new BroadcastConstants(
    Settings.Global.BROADCAST_BG_CONSTANTS);
backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;

final BroadcastConstants offloadConstants = new BroadcastConstants(
    Settings.Global.BROADCAST_OFFLOAD_CONSTANTS);
offloadConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
// by default, no "slow" policy in this queue
offloadConstants.SLOW_TIME = Integer.MAX_VALUE;

// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;

同样,不同类型广播 超时时间也是不一样的。 所以很清晰看到:前台广播超时时间10s,后台广播和offload类型,超时是60s。

Service

在总结四大组件中已经说过,service的启动有两种方式:startService()和bindService() 。这里主要看下startService()的流程,ANR是如何产生的。

//ContextImpl.java
@Override
public ComponentName startService(Intent service) {
    return startServiceCommon(service, false, mUser);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
        UserHandle user) {
    try {
        ComponentName cn = ActivityManager.getService().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), requireForeground,
                        getOpPackageName(), user.getIdentifier());
        return cn;
    }
}

//ActivityManagerService.java
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, boolean requireForeground, String callingPackage, int userId)
        throws TransactionTooLargeException {
        res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid,
                    requireForeground, callingPackage, userId);
        return res;
    }
}

//ActiveServices.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
        throws TransactionTooLargeException {
    return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
            callingPackage, userId, false);
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, boolean fgRequired, String callingPackage,
        final int userId, boolean allowBackgroundActivityStarts)
        throws TransactionTooLargeException {
    ServiceLookupResult res =
        retrieveServiceLocked(service, null, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg, false, false);
    ServiceRecord r = res.record;
    ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    return cmp;
}
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
        boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
    return r.name;
}
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting, boolean permissionsReviewRequired)
        throws TransactionTooLargeException {
    final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
    final String procName = r.processName;
    if (!isolated) {
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (app != null && app.thread != null) {
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                realStartServiceLocked(r, app, execInFg);
                return null;
            }
        }
    }
    return null;
}

很简单清晰,不赘述,这里最终可以看到 走到了realStartServiceLocked()方法,由方法名可以猜测是 真正启动服务的关键,下面继续看,这里如何启动服务的不关注,主要看ANR的产生。

//ActiveServices.java
/**
 * Note the name of this method should not be confused with the started services concept.
 * The "start" here means bring up the instance in the client, and this method is called
 * from bindService() as well.
 */
private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    r.setProcess(app);
    r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
    final boolean newService = app.services.add(r);
    //真正创建服务前执行的
    bumpServiceExecutingLocked(r, execInFg, "create");
    ...
    app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.getReportedProcState());
    ...
    serviceDoneExecutingLocked(r, inDestroying, inDestroying);
}

从注释也能看到,bindService() 也会调用。这里继续看bumpServiceExecutingLocked():

//ActiveServices.java
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
    scheduleServiceTimeoutLocked(r.app);
    ...
    r.executeFg |= fg;
    r.executeNesting++;
    //r.executingStart记录了创建服务开始的时间戳
    r.executingStart = now;
}

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;
    //延迟发送,即超时时间
    mAm.mHandler.sendMessageDelayed(msg,
            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

//ActivityManagerService.java
final class MainHandler extends Handler {
    public MainHandler(Looper looper) {
        super(looper, null, true);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case SERVICE_TIMEOUT_MSG: {
            mServices.serviceTimeout((ProcessRecord)msg.obj);
        } break;
        case SERVICE_FOREGROUND_TIMEOUT_MSG: {
            mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
        } break;
        }
    }
}

//ActiveServices.java
void serviceTimeout(ProcessRecord proc) {
    String anrMessage = null;
    synchronized(mAm) {
        if (proc.executingServices.size() == 0 || proc.thread == null) {
            return;
        }
        final long now = SystemClock.uptimeMillis();
        //当前时间 - timeout时间,即最晚开始时间
        final long maxTime =  now -
                (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
        ServiceRecord timeout = null;
        long nextTime = 0;
        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;
            }
        }
        if (timeout != null && mAm.mProcessList.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);
            //超时 记录的信息,即ANR信息
            anrMessage = "executing service " + timeout.shortInstanceName;
        } else {
            Message msg = mAm.mHandler.obtainMessage(
                    ActivityManagerService.SERVICE_TIMEOUT_MSG);
            msg.obj = proc;
            mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
                    ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
        }
    }

    if (anrMessage != null) {
        //ANR信息传递到进程 处理。
        proc.appNotResponding(null, null, null, null, false, anrMessage);
    }
}

仔细看下上面代码及注释 就能理解。最终超时信息传入到进程进行处理。 proc.appNotResponding(null, null, null, null, false, anrMessage);

这个超时时间定义如下,注释也很清晰,不同类型服务 超时时间是不一样的。

//ActiveServices.java
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
// How long the startForegroundService() grace period is to get around to
// calling startForeground() before we ANR + stop it.
static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;

ContentProvider

在应用启动时,ContentProvider发布 若超时也会发生ANR。

应用启动后,ActivityThread执行attach()操作,最后会执行attachApplicationLocked() 实现上述ANR判断。

//ActivityManagerService.java
// How long we wait for an attached process to publish its content providers
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
/**
 * How long we wait for an provider to be published. Should be longer than
 * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
 */
static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;

@GuardedBy("this")
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
        Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
        msg.obj = app;
        //可能ANR
        mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
    }         
    ...
    try {
        //移除CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG延迟消息
        //这里的thread是从ActivityThread传入的,ApplicationThread对象。
        thread.bindApplication(processName, appInfo, providers, ...);
    }
}


final class MainHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
            ProcessRecord app = (ProcessRecord)msg.obj;
            synchronized (ActivityManagerService.this) {
                processContentProviderPublishTimedOutLocked(app);
            }
        } break;
        }
    }
}

@GuardedBy("this")
private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
    cleanupAppInLaunchingProvidersLocked(app, true);
    mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
}

CONTENT_PROVIDER_PUBLISH_TIMEOUT是10s,10s后发送消息 Hander进行处理,会有timeout publishing content providers信息。在10s内完成,会移除该消息,即不会触发ANR了。

若发生超时,这里没有调用appNotResponding()(不像前3种),这里会杀掉进程并清理了相关信息。

如何移除,看thread.bindApplication(),该方法在延迟发送消息之后执行,即移除延迟消息。如在10s内执行完成,就是不会触发ANR。

注:这是最简单直接看到的一种,移除该消息的调用地方不只一处。

简单看下这个移除延迟消息过程:

//ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {
    public final void bindApplication(String processName, ApplicationInfo appInfo,
            ...) {
        sendMessage(H.BIND_APPLICATION, data);
    }
}

class H extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BIND_APPLICATION:
                AppBindData data = (AppBindData)msg.obj;
                handleBindApplication(data);
                break;
        }
    }
}

@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
    try {
        if (!data.restrictedBackupMode) {
            if (!ArrayUtils.isEmpty(data.providers)) {
                installContentProviders(app, data.providers);
            }
        }
    } 
}

@UnsupportedAppUsage
private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    try {
        ActivityManager.getService().publishContentProviders(
            getApplicationThread(), results);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

//ActivityManagerService.java
public final void publishContentProviders(IApplicationThread caller,
            List<ContentProviderHolder> providers) {
    final ProcessRecord r = getRecordForAppLocked(caller);
    if (wasInLaunchingProviders) {
        mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
    }  
}
posted @ 2021-02-21 21:51  流浪_归家  阅读(1653)  评论(0编辑  收藏  举报