zoukankan      html  css  js  c++  java
  • 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);
        }  
    }
    
    善始者实繁,克终者盖寡。 ---不足或不对的地方欢迎指正。
  • 相关阅读:
    工厂方法模式
    代理模式
    观察者模式
    策略模式
    单例模式
    简单工厂模式
    lintcode:等价二叉树
    lintcode:被围绕的区域
    lintcode:二叉树的所有路径
    lintcode:快乐数
  • 原文地址:https://www.cnblogs.com/fanglongxiang/p/14427405.html
Copyright © 2011-2022 走看看