ANR詳解

說到ANR,首先提出以下三個問題

  • ANR是什么
  • ANR什么時候出現(xiàn)
  • ANR發(fā)生的原理

帶著這三個問題我們進入本次的話題


ANR是什么

ANR全稱Application Not Response,應用無響應。指的是一定時間內(nèi)處理某一事件超過一定時間就會彈出應用無響應的彈框,用于用戶可以強制結(jié)束進程。

ANR什么時候出現(xiàn)

  • InputDispatch響應時間超過5秒
  • BroadCastReceiver 前臺響應時間超過10秒,后臺響應時間超過60秒
  • Service 前臺響應時間超過20秒,后臺響應時間超過200秒
  • ContentProvider響應時間超過10秒

ANR發(fā)生的原理

針對上面4種情況接下來一一分析:

1. InputDispatch ANR

Android底層接收處理觸摸事件主要依賴
InputReader.cpp和InputDispatcher.cpp兩大類。這里先簡單介紹下這兩個類
首先來看InputReader.cpp

/frameworks/native/services/inputflinger/InputReader.cpp

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector<InputDeviceInfo> inputDevices;
    { // acquire lock
       ...代碼省略...

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    ...代碼省略...

    // Flush queued events out to the listener.
    // This must happen outside of the lock because the listener could potentially call
    // back into the InputReader's methods, such as getScanCodeState, or become blocked
    // on another thread similarly waiting to acquire the InputReader lock thereby
    // resulting in a deadlock.  This situation is actually quite plausible because the
    // listener is actually the input dispatcher, which calls into the window manager,
    // which occasionally calls into the input reader.
    mQueuedListener->flush();
}

本身做loop循環(huán),然后通過EventHub#getEvents方法獲取指定事件,然后生成事件,加入到隊列,通知喚醒InputDispatcher(此處詳情不再展開)

然后介紹InputDispatcher.cpp

/frameworks/native/services/inputflinger/InputDispatcher.cpp

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce(); //【見小節(jié)1.2】
    return true;
}

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock

    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

與InputReader類似,自身開啟一個Loop循環(huán),然后當隊列不為空時,開始執(zhí)行dispatchOnceInnerLocked-> dispatchKeyLocked-> findFocusedWindowTargetsLocked-> handleTargetsNotReadyLocked.
接下來我們重點關(guān)注下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) {
   ...代碼省略...
            nsecs_t timeout;
            if (windowHandle != NULL) {
                timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            } else if (applicationHandle != NULL) {
                timeout = applicationHandle->getDispatchingTimeout(
                        DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            } else {
                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
            }

     ...代碼省略...
    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);

        // Force poll loop to wake up immediately on next iteration once we get the
        // ANR response back from the policy.
        *nextWakeupTime = LONG_LONG_MIN;
        return INPUT_EVENT_INJECTION_PENDING;
    } else {
        // Force poll loop to wake up when timeout is due.
        if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
            *nextWakeupTime = mInputTargetWaitTimeoutTime;
        }
        return INPUT_EVENT_INJECTION_PENDING;
    }
}

從中我們可以看到兩點:

  1. DEFAULT_INPUT_DISPATCHING_TIMEOUT這個值為5秒
  2. currentTime >= mInputTargetWaitTimeoutTime 代表超過5秒的時候就要處理相應的事件了
    即onANRLocked方法(看名字就是ANR的處理了)
    然后會執(zhí)行doNotifyANRLockedInterruptible方法
void InputDispatcher::doNotifyANRLockedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();

    nsecs_t newTimeout = mPolicy->notifyANR(
            commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
            commandEntry->reason);

    mLock.lock();

    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
            commandEntry->inputWindowHandle != NULL
                    ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
}

接下來會執(zhí)行mPolicy的notifyANR方法。
此處的mPolicy便是com_android_server_input_InputManagerService

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
看這名字感覺已經(jīng)快到Java層了,我們慢慢往下看:

nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
        const sp<InputWindowHandle>& inputWindowHandle, const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
    ALOGD("notifyANR");
#endif
    ATRACE_CALL();

    JNIEnv* env = jniEnv();

    jobject inputApplicationHandleObj =
            getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
    jobject inputWindowHandleObj =
            getInputWindowHandleObjLocalRef(env, inputWindowHandle);
    jstring reasonObj = env->NewStringUTF(reason.c_str());

    jlong newTimeout = env->CallLongMethod(mServiceObj,
                gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj,
                reasonObj);
    if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
        newTimeout = 0; // abort dispatch
    } else {
        assert(newTimeout >= 0);
    }

    env->DeleteLocalRef(reasonObj);
    env->DeleteLocalRef(inputWindowHandleObj);
    env->DeleteLocalRef(inputApplicationHandleObj);
    return newTimeout;
}

關(guān)鍵代碼為 env->CallLongMethod 。 此處正是C++ 層調(diào)用Java代碼也就是執(zhí)行了gServiceClassInfo的notifyANR方法,此處的gServiceClassInfo便是InputManagerService。那么我們繼續(xù)往Java層看:

    private long notifyANR(InputApplicationHandle inputApplicationHandle,
            InputWindowHandle inputWindowHandle, String reason) {
        return mWindowManagerCallbacks.notifyANR(
                inputApplicationHandle, inputWindowHandle, reason);
    }

這里的處理邏輯很簡單,執(zhí)行了mWindowManagerCallbacks的notifyANR方法,InputMonitor實現(xiàn)了WindowManagerCallbacks,所以這里便進入了InputMonitor類:

    @Override
    public long notifyANR(InputApplicationHandle inputApplicationHandle,
            InputWindowHandle inputWindowHandle, String reason) {
        ...代碼省略...

        if (appWindowToken != null && appWindowToken.appToken != null) {
            // Notify the activity manager about the timeout and let it decide whether
            // to abort dispatching or keep waiting.
            ...代碼省略...
        } else if (windowState != null) {
            try {
                // Notify the activity manager about the timeout and let it decide whether
                // to abort dispatching or keep waiting.
                long timeout = ActivityManager.getService().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
                }
            } catch (RemoteException ex) {
            }
        }
        return 0; // abort dispatching
    }

當WindowState不為空的時候執(zhí)行ActivityManager.getService().inputDispatchingTimedOut方法即ActivityManagerService#inputDispatchingTimedOut。(這里的WindowState即代表一個真正的 窗口。,此處不做展開)。


    @Override
    public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
        if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires permission "
                    + android.Manifest.permission.FILTER_EVENTS);
        }
        ProcessRecord proc;
        long timeout;
        synchronized (this) {
            synchronized (mPidsSelfLocked) {
                proc = mPidsSelfLocked.get(pid);
            }
            timeout = getInputDispatchingTimeoutLocked(proc);
        }

        if (inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
            return -1;
        }

        return timeout;
    }

然后繼續(xù)執(zhí)行inputDispatchingTimedOut方法

    public boolean inputDispatchingTimedOut(final ProcessRecord proc,
            final ActivityRecord activity, final ActivityRecord parent,
            final boolean aboveSystem, String reason) {
        ...代碼省略...

        if (proc != null) {
            synchronized (this) {
                if (proc.debugging) {    
                    //當程序Debug的情況下,不彈出ANR窗口
                    return false;
                }

                if (proc.instr != null) {
                    //當前進程為子進程的時候,直接kill掉子進程,不彈出ANR窗口
                    Bundle info = new Bundle();
                    info.putString("shortMsg", "keyDispatchingTimedOut");
                    info.putString("longMsg", annotation);
                    finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
                    return true;
                }
            }
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
                }
            });
        }

        return true;
    }

執(zhí)行mAppErrors.appNotResponding方法:

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

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

            if (isSilentANR) {
                app.kill("bg anr", true);
                return;
            }

            // Set the app's notResponding state, and look up the errorReportReceiver
            makeAppNotRespondingLocked(app,
                    activity != null ? activity.shortComponentName : null,
                    annotation != null ? "ANR " + annotation : "ANR",
                    info.toString());

            // Bring up the infamous App Not Responding dialog
            Message msg = Message.obtain();
            msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
            msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);

            mService.mUiHandler.sendMessage(msg);
        }
    }

這段代碼非常長,直接省略部分代碼,看最后,通過UIHandler發(fā)送了一個SHOW_NOT_RESPONDING_UI_MSG通知。我們找到初始化這個UIHandler的地方看看他的dispatchMessage 吧

    final class UiHandler extends Handler {
        public UiHandler() {
            super(com.android.server.UiThread.get().getLooper(), null, true);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case SHOW_ERROR_UI_MSG: {
                mAppErrors.handleShowAppErrorUi(msg);
                ensureBootCompleted();
            } break;
            case SHOW_NOT_RESPONDING_UI_MSG: {
                mAppErrors.handleShowAnrUi(msg);
                ensureBootCompleted();
            } break;
            ...代碼省略...
        }
}

看SHOW_NOT_RESPONDING_UI_MSG是執(zhí)行了mAppErrors.handleShowAnrUi方法:

   void handleShowAnrUi(Message msg) {
        Dialog dialogToShow = null;
        synchronized (mService) {
            ... 代碼省略...
            if (mService.canShowErrorDialogs() || showBackground) {
                dialogToShow = new AppNotRespondingDialog(mService, mContext, data);
                proc.anrDialog = dialogToShow;
            } else {
                MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
                        AppNotRespondingDialog.CANT_SHOW);
                // Just kill the app if there is no dialog to be shown.
                mService.killAppAtUsersRequest(proc, null);
            }
        }
        // If we've created a crash dialog, show it without the lock held
        if (dialogToShow != null) {
            dialogToShow.show();
        }
    }

終于到了最后一步了,這里的dialogToShow正是我們非常熟悉的ANR提示窗口了,最終就會彈出ANR窗口。下面給出對應的時序圖:


input dispatch ANR.jpg

2. BroadCastReceiver ANR

同樣的我們從BroadcastReceiver啟動源頭開始sendBroadcast說起,調(diào)用這個方法的時候會調(diào)用到ContextWrapper的sendBroadcast方法:

    @Override
    public void sendBroadcast(Intent intent) {
        mBase.sendBroadcast(intent);
    }

然后會執(zhí)行mBase的sendBroadcast方法,那么這個mBase是什么呢,這就要從Activity啟動流程開始說起,本文篇幅有限直接進入ActivityThread#performLaunchActivity()方法:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...代碼省略...
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            ...代碼省略...
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }

這里Activity會執(zhí)行attach方法將appContext傳入然后執(zhí)行attachBaseContent將appContent賦值給mBase。而此處的appContext就是ContextImpl。那么就進入ContextImp看下:

    @Override
    public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess(this);
            ActivityManager.getService().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                    getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

處理也是非常簡單,直接調(diào)起了ActivityManagerService的broadcastIntent方法:

public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle resultExtras,
            String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean serialized, boolean sticky, int userId) {
        enforceNotIsolatedCaller("broadcastIntent");
        synchronized(this) {
            intent = verifyBroadcastLocked(intent);

            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, appOp, bOptions, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

然后調(diào)用broadcastIntentLocked,這里的代碼過長就不再展示,然后是調(diào)用了BroadcastQueue#scheduleBroadcastsLocked方法:

    public void scheduleBroadcastsLocked() {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                + mQueueName + "]: current="
                + mBroadcastsScheduled);

        if (mBroadcastsScheduled) {
            return;
        }
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }

我們可以看到它是通過Handler發(fā)送了一條消息:

    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: {
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                    processNextBroadcast(true);
                } break;
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
            }
        }
    }

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

跟到消息處理里面,然后執(zhí)行processNextBroadcast->processNextBroadcastLocked方法:

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
         // Keep track of when this receiver started, and make sure there
        // is a timeout message pending to kill it if need be.
        r.receiverTime = SystemClock.uptimeMillis();
        if (! mPendingBroadcastTimeoutMessage) {
              //mPendingBroadcastTimeoutMessage這個參數(shù)只有在已經(jīng)發(fā)送了延時消息的時候才會設置成true目的是為了防止重新發(fā)送延時消息。這個延時時間點正式超時時間
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                    "Submitting BROADCAST_TIMEOUT_MSG ["
                    + mQueueName + "] for " + r + " at " + timeoutTime);
            setBroadcastTimeoutLocked(timeoutTime);
        }
}

這里面我們可以看下mTimeoutPeriod,因為延時消息發(fā)送的時候是將當前時間+mTimeoutPeriod這個時間的。搜了一下發(fā)現(xiàn)是在初始化對象的時候?qū)⒅祩魅氲?

    BroadcastQueue(ActivityManagerService service, Handler handler,
            String name, long timeoutPeriod, boolean allowDelayBehindServices) {
        mService = service;
        mHandler = new BroadcastHandler(handler.getLooper());
        mQueueName = name;
        mTimeoutPeriod = timeoutPeriod;
        mDelayBehindServices = allowDelayBehindServices;
    }

再去看下什么時候初始化:

        mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                "foreground", BROADCAST_FG_TIMEOUT, false);
        mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
                "background", BROADCAST_BG_TIMEOUT, true);

發(fā)現(xiàn)有兩種類型的廣播隊列初始化,一種是前臺的,一種是后臺的

    // 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,后臺廣播對應的超時時間是60s,然后一旦延時消息執(zhí)行的時候會調(diào)用下面的方法:

    final void broadcastTimeoutLocked(boolean fromMsg) {
        if (fromMsg) {
            //從延時消息過來的時候,將這個值設置為false,解鎖操作
            mPendingBroadcastTimeoutMessage = false;
        }

        if (mOrderedBroadcasts.size() == 0) {
            //當前廣播已經(jīng)處理完了,那么就不需要彈出ANR了
            return;
        }

       ...代碼省略...

        if (app != null) {
            anrMessage = "Broadcast of " + r.intent.toString();
        }

        if (mPendingBroadcast == r) {
            mPendingBroadcast = null;
        }

        // Move on to the next receiver.
        finishReceiverLocked(r, r.resultCode, r.resultData,
                r.resultExtras, r.resultAbort, false);
        scheduleBroadcastsLocked();

        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));
        }
    }

當當前不屬于debug狀態(tài),并且ANR信息不為空的時候調(diào)用 mHandler.post(new AppNotResponding(app, anrMessage));,這個方法是不是很熟悉,就跟前面手勢事件超時最后調(diào)用的地方一致會執(zhí)行mAppErrors.appNotResponding操作,最終彈出ANR窗口,這里給出對應的流程圖:


BroadcastReceiver ANR.png

3. Service ANR

ServiceANR原理與BroadcastReceiver類似,那么也從源頭開始說起吧:

    @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }

類似的啟動方式,然后跳到ContextImpl中:

    @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }

    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
            if (cn != null) {
                if (cn.getPackageName().equals("!")) {
                    throw new SecurityException(
                            "Not allowed to start service " + service
                            + " without permission " + cn.getClassName());
                } else if (cn.getPackageName().equals("!!")) {
                    throw new SecurityException(
                            "Unable to start service " + service
                            + ": " + cn.getClassName());
                } else if (cn.getPackageName().equals("?")) {
                    throw new IllegalStateException(
                            "Not allowed to start service " + service + ": " + cn.getClassName());
                }
            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

也是一樣最終會進入ActivityManagerService中startService

    @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage, int userId)
            throws TransactionTooLargeException {
        enforceNotIsolatedCaller("startService");
        // Refuse possible leaked file descriptors
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }

ActivityManagerService最終會調(diào)用ActiveServices的startServiceLocked->startServiceInnerLocked:

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
        ServiceState stracker = r.getTracker();
        if (stracker != null) {
            stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
        }
        r.callStart = false;
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startRunningLocked();
        }
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
        ..代碼省略...

        return r.name;
    }

然后就是執(zhí)行bringUpServiceLocked方法,此方法里面會真正開始啟動service,也就是會執(zhí)行realStartServiceLocked方法:

private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        ...代碼省略...
        //此處處理方式與BroadcastReceiver相似,都是先獲取當前時間,然后加上超時所需要的時間就是最后的超時時間
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
        //這個地方會先發(fā)送一個超時消息
        bumpServiceExecutingLocked(r, execInFg, "create");
        ...代碼省略...
        try {
            ...代碼省略...
            mAm.notifyPackageUse(r.serviceInfo.packageName,
                                 PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            //app.thread就是ApplicationThread,就是ActivityThread的子類
            //然后scheduleCreateService就會執(zhí)行Service的onCreate方法
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
            Slog.w(TAG, "Application dead when creating service " + r);
            mAm.appDiedLocked(app);
            throw e;
        } finally {
            ...代碼省略...
        }

        ...代碼省略...
    }

在調(diào)用Service的onCreate方法前會先調(diào)用bumpServiceExecutingLocked發(fā)送超時消息

    private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
        ...代碼省略...
        if (r.executeNesting == 0) {
            ...代碼省略...
        } else if (r.app != null && fg && !r.app.execServicesFg) {
            r.app.execServicesFg = true;
            if (timeoutNeeded) {
                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;
        mAm.mHandler.sendMessageDelayed(msg,
                proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
    }

    // 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;

在scheduleServiceTimeoutLocked方法中會先判斷Service是到底是屬于前臺Service還是后臺Service,前臺Service超時時間是20s,后臺Service超時時間是200s.
發(fā)送超時消息以后,會在ActivityManagerService里面接收到此消息,會執(zhí)行ActiveServices的serviceTimeout方法:

  void serviceTimeout(ProcessRecord proc) {
        ...代碼省略...

        if (anrMessage != null) {
            mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
        }
    }

最后無一例外都會調(diào)用AppErrors的appNotResponding方法,后續(xù)就不在跟進了。此處給出時序圖:


Service ANR.png

4. ContentProvider ANR

ContentProvider啟動的流程就有點不太一致了,它的入口是ActivityThread的main方法(這也是整個APP啟動的入口):

    public static void main(String[] args) {
        ...代碼省略...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        ...代碼省略...
    }

ContentProvider就從thread.attach方法開始的:

private void attach(boolean system, long startSeq) {
            ...代碼省略
            final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread, startSeq);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            ...代碼省略...
}

這里也是交給了ActivityManagerService來處理,調(diào)用了attachApplication方法:

    @Override
    public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            Binder.restoreCallingIdentity(origId);
        }
    }

接著就會執(zhí)行attachApplicationLocked方法:

    @GuardedBy("this")
    private final boolean attachApplicationLocked(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;
            mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
        }

        ...代碼省略...

        return true;
    }

    // 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;

這里會發(fā)送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG的延時消息,延時時間為CONTENT_PROVIDER_PUBLISH_TIMEOUT即10秒。然后延時消息里面會執(zhí)行processContentProviderPublishTimedOutLocked方法:

     @GuardedBy("this")
    boolean removeProcessLocked(ProcessRecord app,
            boolean callerWillRestart, boolean allowRestart, String reason) {
       ...代碼省略...
        boolean needRestart = false;
        if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) {
            ...代碼省略...
            app.kill(reason, true);
            handleAppDiedLocked(app, willRestart, allowRestart);
            if (willRestart) {
                removeLruProcessLocked(app);
                addAppLocked(app.info, null, false, null /* ABI override */);
            }
        } else {
            mRemovedProcesses.add(app);
        }

        return needRestart;
    }

與其他三種情況不一樣的是,ContentProvider在超時的時候并不會彈出ANR的Dialog,而是直接干掉了進程,下面給出ContentProvider超時的時序圖:


ContentProvider ANR.png

從上面四種ANR流程可以看出一個共同點,那就是不管以何種方式出現(xiàn)超時處理,都需要經(jīng)過AMS,由此也可以看出AMS作為一個核心類的重要性

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容