ANR詳解

說到ANR前域,首先提出以下三個(gè)問題

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

帶著這三個(gè)問題我們進(jìn)入本次的話題


ANR是什么

ANR全稱Application Not Response,應(yīng)用無響應(yīng)韵吨。指的是一定時(shí)間內(nèi)處理某一事件超過一定時(shí)間就會(huì)彈出應(yīng)用無響應(yīng)的彈框匿垄,用于用戶可以強(qiáng)制結(jié)束進(jìn)程。

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

  • InputDispatch響應(yīng)時(shí)間超過5秒
  • BroadCastReceiver 前臺(tái)響應(yīng)時(shí)間超過10秒归粉,后臺(tái)響應(yīng)時(shí)間超過60秒
  • Service 前臺(tái)響應(yīng)時(shí)間超過20秒椿疗,后臺(tái)響應(yīng)時(shí)間超過200秒
  • ContentProvider響應(yīng)時(shí)間超過10秒

ANR發(fā)生的原理

針對(duì)上面4種情況接下來一一分析:

1. InputDispatch ANR

Android底層接收處理觸摸事件主要依賴
InputReader.cpp和InputDispatcher.cpp兩大類。這里先簡單介紹下這兩個(gè)類
首先來看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方法獲取指定事件届榄,然后生成事件,加入到隊(duì)列倔喂,通知喚醒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類似铝条,自身開啟一個(gè)Loop循環(huán),然后當(dāng)隊(duì)列不為空時(shí)席噩,開始執(zhí)行dispatchOnceInnerLocked-> dispatchKeyLocked-> findFocusedWindowTargetsLocked-> handleTargetsNotReadyLocked.
接下來我們重點(diǎn)關(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;
    }
}

從中我們可以看到兩點(diǎn):

  1. DEFAULT_INPUT_DISPATCHING_TIMEOUT這個(gè)值為5秒
  2. currentTime >= mInputTargetWaitTimeoutTime 代表超過5秒的時(shí)候就要處理相應(yīng)的事件了
    即onANRLocked方法(看名字就是ANR的處理了)
    然后會(huì)執(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);
}

接下來會(huì)執(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實(shí)現(xiàn)了WindowManagerCallbacks假丧,所以這里便進(jìn)入了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
    }

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


    @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) {    
                    //當(dāng)程序Debug的情況下,不彈出ANR窗口
                    return false;
                }

                if (proc.instr != null) {
                    //當(dāng)前進(jìn)程為子進(jìn)程的時(shí)候运吓,直接kill掉子進(jìn)程渴邦,不彈出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ā)送了一個(gè)SHOW_NOT_RESPONDING_UI_MSG通知。我們找到初始化這個(gè)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提示窗口了瓮床,最終就會(huì)彈出ANR窗口。下面給出對(duì)應(yīng)的時(shí)序圖:


input dispatch ANR.jpg

2. BroadCastReceiver ANR

同樣的我們從BroadcastReceiver啟動(dòng)源頭開始sendBroadcast說起,調(diào)用這個(gè)方法的時(shí)候會(huì)調(diào)用到ContextWrapper的sendBroadcast方法:

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

然后會(huì)執(zhí)行mBase的sendBroadcast方法,那么這個(gè)mBase是什么呢产镐,這就要從Activity啟動(dòng)流程開始說起隘庄,本文篇幅有限直接進(jìn)入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會(huì)執(zhí)行attach方法將appContext傳入然后執(zhí)行attachBaseContent將appContent賦值給mBase。而此處的appContext就是ContextImpl癣亚。那么就進(jìn)入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這個(gè)參數(shù)只有在已經(jīng)發(fā)送了延時(shí)消息的時(shí)候才會(huì)設(shè)置成true目的是為了防止重新發(fā)送延時(shí)消息。這個(gè)延時(shí)時(shí)間點(diǎn)正式超時(shí)時(shí)間
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                    "Submitting BROADCAST_TIMEOUT_MSG ["
                    + mQueueName + "] for " + r + " at " + timeoutTime);
            setBroadcastTimeoutLocked(timeoutTime);
        }
}

這里面我們可以看下mTimeoutPeriod,因?yàn)檠訒r(shí)消息發(fā)送的時(shí)候是將當(dāng)前時(shí)間+mTimeoutPeriod這個(gè)時(shí)間的玻孟。搜了一下發(fā)現(xiàn)是在初始化對(duì)象的時(shí)候?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;
    }

再去看下什么時(shí)候初始化:

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

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

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

前臺(tái)廣播對(duì)應(yīng)的超時(shí)時(shí)間是10s,后臺(tái)廣播對(duì)應(yīng)的超時(shí)時(shí)間是60s取募,然后一旦延時(shí)消息執(zhí)行的時(shí)候會(huì)調(diào)用下面的方法:

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

        if (mOrderedBroadcasts.size() == 0) {
            //當(dāng)前廣播已經(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));
        }
    }

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


BroadcastReceiver ANR.png

3. Service ANR

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

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

類似的啟動(dòng)方式糕篇,然后跳到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();
        }
    }

也是一樣最終會(huì)進(jìn)入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最終會(huì)調(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方法,此方法里面會(huì)真正開始啟動(dòng)service术吝,也就是會(huì)執(zhí)行realStartServiceLocked方法:

private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        ...代碼省略...
        //此處處理方式與BroadcastReceiver相似,都是先獲取當(dāng)前時(shí)間饭入,然后加上超時(shí)所需要的時(shí)間就是最后的超時(shí)時(shí)間
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
        //這個(gè)地方會(huì)先發(fā)送一個(gè)超時(shí)消息
        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就會(huì)執(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方法前會(huì)先調(diào)用bumpServiceExecutingLocked發(fā)送超時(shí)消息

    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方法中會(huì)先判斷Service是到底是屬于前臺(tái)Service還是后臺(tái)Service肛真,前臺(tái)Service超時(shí)時(shí)間是20s,后臺(tái)Service超時(shí)時(shí)間是200s.
發(fā)送超時(shí)消息以后谐丢,會(huì)在ActivityManagerService里面接收到此消息,會(huì)執(zhí)行ActiveServices的serviceTimeout方法:

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

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

最后無一例外都會(huì)調(diào)用AppErrors的appNotResponding方法蚓让,后續(xù)就不在跟進(jìn)了乾忱。此處給出時(shí)序圖:


Service ANR.png

4. ContentProvider ANR

ContentProvider啟動(dòng)的流程就有點(diǎn)不太一致了,它的入口是ActivityThread的main方法(這也是整個(gè)APP啟動(dòng)的入口):

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

接著就會(huì)執(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;

這里會(huì)發(fā)送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG的延時(shí)消息窄瘟,延時(shí)時(shí)間為CONTENT_PROVIDER_PUBLISH_TIMEOUT即10秒。然后延時(shí)消息里面會(huì)執(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在超時(shí)的時(shí)候并不會(huì)彈出ANR的Dialog蹄葱,而是直接干掉了進(jìn)程,下面給出ContentProvider超時(shí)的時(shí)序圖:


ContentProvider ANR.png

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末右蕊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吮螺,更是在濱河造成了極大的恐慌饶囚,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸠补,死亡現(xiàn)場(chǎng)離奇詭異萝风,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)紫岩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門规惰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人泉蝌,你說我怎么就攤上這事歇万。” “怎么了勋陪?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵贪磺,是天一觀的道長。 經(jīng)常有香客問我诅愚,道長寒锚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮刹前,結(jié)果婚禮上泳赋,老公的妹妹穿的比我還像新娘。我一直安慰自己腮郊,他們只是感情好摹蘑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著轧飞,像睡著了一般衅鹿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上过咬,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天大渤,我揣著相機(jī)與錄音,去河邊找鬼掸绞。 笑死泵三,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的衔掸。 我是一名探鬼主播烫幕,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼敞映!你這毒婦竟也來了较曼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤振愿,失蹤者是張志新(化名)和其女友劉穎捷犹,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冕末,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萍歉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了档桃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枪孩。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖藻肄,靈堂內(nèi)的尸體忽然破棺而出销凑,到底是詐尸還是另有隱情,我是刑警寧澤仅炊,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布斗幼,位于F島的核電站,受9級(jí)特大地震影響抚垄,放射性物質(zhì)發(fā)生泄漏蜕窿。R本人自食惡果不足惜谋逻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望桐经。 院中可真熱鬧毁兆,春花似錦、人聲如沸阴挣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽畔咧。三九已至茎芭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間誓沸,已是汗流浹背梅桩。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拜隧,地道東北人宿百。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像洪添,于是被迫代替她去往敵國和親垦页。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353