An overview,

ANR(Application Not responding), refers to the Application does Not respond, the Android system for some events need to be completed within a certain time range, if the scheduled time can Not get an effective response or response time is too long, will cause ANR. Generally, a prompt box will pop up to inform the user that XXX is not responding. The user can continue to wait or Force Close.

So what are the scenarios that lead to ANR?

  • Service Timeout: For example, the foreground Service is not executed within 20 seconds.
  • BroadcastQueue Timeout: for example, BroadcastQueue Timeout is not completed within 10 seconds
  • ContentProvider Timeout: the ContentProvider publishes a Timeout 10 seconds;
  • InputDispatching Timeout: The input event dispatching Timeout is 5s, including key strokes and touch events.

The process of triggering ANR can be divided into three steps: planting the bomb, disarming the bomb, and detonating the bomb

Second, the Service

Service Timeout is triggered when the ams. MainHandler in the “ActivityManager” thread receives the SERVICE_TIMEOUT_MSG message.

There are two types of services:

  • For foreground services, the timeout is SERVICE_TIMEOUT = 20s;
  • For background services, the timeout value is SERVICE_BACKGROUND_TIMEOUT = 200s

By the variable ProcessRecord. ExecServicesFg to decide whether to start at the front desk

2.1 bomb

StartService Process Analysis Describes the Service startup process in detail. The realStartServiceLocked() method is called to bury the bomb while the Service attaches to the system_server process.

2.1.1 AS.realStartServiceLocked

[-> ActiveServices.java]

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { ... / / send delay messages (SERVICE_TIMEOUT_MSG), see section 2.1.2 bumpServiceExecutingLocked 】 (r,execInFg, "create"); try { ... / / final execution services onCreate () method of the app. The thread. ScheduleCreateService (r, r.s erviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState); } catch (DeadObjectException e) { mAm.appDiedLocked(app); throw e; } finally { ... }}Copy the code

2.1.2 the AS. BumpServiceExecutingLocked

private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
    ... 
    scheduleServiceTimeoutLocked(r.app);
}

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return; } long now = SystemClock.uptimeMillis(); Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_TIMEOUT_MSG); msg.obj = proc; // If the SERVICE_TIMEOUT_MSG message is not removed after the timeout, Performs a service Timeout process mAm. See 2.3.1 】 【 mHandler. SendMessageAtTime (MSG, proc. ExecServicesFg? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT)); }Copy the code

The method’s main job is to send the delay message (SERVICE_TIMEOUT_MSG). The bomb is planted, we don’t want it to go off, so we need to defuse it before it goes off.

2.2 bomb

In the process AS system_server. RealStartServiceLocked () call will be planted a bomb, the process of the timeout does not start to finish will explode. So when will this bomb be defused? The process of calling handleCreateService(), the main thread of the target process, through Binder and other layers.

2.2.1 palawan handleCreateService

[-> ActivityThread.java]

private void handleCreateService(CreateServiceData data) { ... java.lang.ClassLoader cl = packageInfo.getClassLoader(); Service service = (Service) cl.loadClass(data.info.name).newInstance(); . Try {/ / create ContextImpl object ContextImpl context. = ContextImpl createAppContext (this, packageInfo); context.setOuterContext(service); / / create the Application object Application app = packageInfo makeApplication (false, mInstrumentation); service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault()); // Call the service onCreate() method service.oncreate (); / / fuses [see section 2.2.2] ActivityManagerNative bomb was defused. GetDefault () serviceDoneExecuting (data token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } catch (Exception e) { ... }}Copy the code

The target service object is created and the onCreate() method is called back to system_server several times to execute serviceDoneExecuting.

2.2.2 AS.serviceDoneExecutingLocked

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) {
    ...
    if (r.executeNesting <= 0) {
        if(r.app ! = null) { r.app.execServicesFg =false;
            r.app.executingServices.remove(r);
            if(of state Richard armitage pp. ExecutingServices. The size () = = 0) {/ / the current service without being performed in the process of service mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app); . }... }Copy the code

The main job of this method is to remove the service timeout message SERVICE_TIMEOUT_MSG when the service has finished starting.

2.3 Detonating bombs

Before the introduction of the process of planting and disarming the bomb, if the bomb is successfully disarmed before the end of the countdown, then there is no chance of exploding, but you never know. There are always extreme cases where the bomb cannot be defused immediately, resulting in the explosion of the bomb, which results in the occurrence of App ANR. Here’s a look at where the bomb went off:

There is a Handler thread in the system_server process called “ActivityManager”. When the countdown ends, a message is sent to the Handler thread, SERVICE_TIMEOUT_MSG,

2.3.1 MainHandler. HandleMessage

[-> ActivityManagerService.java ::MainHandler]

final class MainHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            caseSERVICE_TIMEOUT_MSG: { ... // [see section 2.3.2] mservices. serviceTimeout((ProcessRecord)msg.obj); }break; . }... }}Copy the code

AS 2.3.2. ServiceTimeout

void serviceTimeout(ProcessRecord proc) {
    String anrMessage = null;

    synchronized(mAm) {
        if (proc.executingServices.size() == 0 || proc.thread == null) {
            return;
        }
        final long now = SystemClock.uptimeMillis();
        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.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);
            anrMessage = "executing service "+ timeout.shortName; }}if(anrMessage ! AppNotResponding (proc, null, null, proc) {// appNotResponding mAm. AppNotResponding (proc, null, null, proc) {// appNotResponding mAm.false, anrMessage); }}Copy the code

AnrMessage is executing Service [Sending timeout serviceRecord information].

Three BroadcastReceiver

BroadcastReceiver Timeout is located in the “ActivityManager” thread of BroadcastQueue. Trigger BROADCAST_TIMEOUT_MSG BroadcastHandler received the news.

There are two foreground queues and background queues for broadcast queues:

  • For foreground broadcasts, the timeout is BROADCAST_FG_TIMEOUT = 10s;
  • For background broadcasts, the timeout is BROADCAST_BG_TIMEOUT = 60s

3.1 bomb

This article analyzes the Broadcast mechanism of Android Broadcast in detail. The Broadcast start process is processed by calling processNextBroadcast. The process is to process the parallel broadcast first, process the current ordered broadcast, and finally obtain and process the next ordered broadcast.

3.1.1 processNextBroadcast

[-> BroadcastQueue.java]

final void processNextBroadcast(boolean fromMsg) { synchronized(mService) { ... // Part 2: Process the current ordered broadcastdo{ r = mOrderedBroadcasts.get(0); // Get all receivers of the broadcast int numReceivers = (R.extvers! = null) ? r.receivers.size() : 0;if (mService.mProcessesReady && r.dispatchTime > 0) {
                long now = SystemClock.uptimeMillis();
                if(numReceivers > 0) && (now > r.dispatchTime + (2* Mtimeout *numReceivers)) { [see section 3.3.2] broadcastTimeoutLocked(false); . }}if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                if(r.resultTo ! = null) {// Handle broadcast message message performReceiveLocked(R.C allerApp, R.tesultto, new Intent(R.intent), r.tesultCode, R.tesultData, r.resultExtras,false.false, r.userId); r.resultTo = null; } / / bomb [see section 3.2.2] cancelBroadcastTimeoutLocked (); }}while(r == null); . // Part 3: Get the next ordered broadcast r.riceiverTime = systemclock. uptimeMillis();if(! mPendingBroadcastTimeoutMessage) { long timeoutTime = r.receiverTime + mTimeoutPeriod; // Bury the bomb [see section 3.1.2]setBroadcastTimeoutLocked(timeoutTime); }... }}Copy the code

For broadcast timeout processing time:

  1. In the first place in the process of part3 setBroadcastTimeoutLocked (timeoutTime) set the timeout broadcast news;
  2. Then in part2, according to the broadcast processing:
    • Call broadcastTimeoutLocked(false) when broadcast receivers wait too long.
    • When after the broadcast, call cancelBroadcastTimeoutLocked;

3.1.2 setBroadcastTimeoutLocked

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

Set BROADCAST_TIMEOUT_MSG, that is, the broadcast timeout process is started when the broadcast is not complete.

3.2 bomb

Broadcast is similar to the Service timeout mechanism, but with a subtle skill point that statically registered broadcast timeouts are affected by SharedPreferences(SP).

3.2.1 sendFinished

The situation regarding whether the broadcast considers SP depends on the following code:

public final void finish() {
    if (mType == TYPE_COMPONENT) {
        final IActivityManager mgr = ActivityManager.getService();
        if(QueuedWork hasPendingWork ()) {/ / when the SP have not synchronous to disk work, need to wait for its completion, to inform system has completed the radio QueuedWork queue (newRunnable() {
                public void run() { sendFinished(mgr); }},false);
        } else{ sendFinished(mgr); }}else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
        final IActivityManager mgr = ActivityManager.getService();
        sendFinished(mgr);
    }
}
Copy the code

As can be seen, only the broadcast timeout detection process of XML static registration takes into account whether any SP has not completed, and dynamic broadcast is not affected by it.

3.2.2 cancelBroadcastTimeoutLocked

final void cancelBroadcastTimeoutLocked() {
    if (mPendingBroadcastTimeoutMessage) {
        mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
        mPendingBroadcastTimeoutMessage = false; }}Copy the code

Remove broadcast timeout message BROADCAST_TIMEOUT_MSG

3.3 Detonating a Bomb

3.3.1 BroadcastHandler.handleMessage

[-> BroadcastQueue.java ::BroadcastHandler]

private final class BroadcastHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            caseBROADCAST_TIMEOUT_MSG: {synchronized (mService) {// [see section 3.3.2] broadcastTimeoutLocked(true); }}break; . }... }}Copy the code

3.3.2 rainfall distribution on 10-12 broadcastTimeoutLocked

[-> BroadcastRecord.java]

//fromMsg = true
final void broadcastTimeoutLocked(boolean fromMsg) {
    if (fromMsg) {
        mPendingBroadcastTimeoutMessage = false;
    }

    if (mOrderedBroadcasts.size() == 0) {
        return;
    }

    long now = SystemClock.uptimeMillis();
    BroadcastRecord r = mOrderedBroadcasts.get(0);
    if (fromMsg) {
        if (mService.mDidDexOpt) {
            mService.mDidDexOpt = false;
            long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
            setBroadcastTimeoutLocked(timeoutTime);
            return;
        }

        if(! mService.mProcessesReady) {return; // when the system is not ready, there is no timeout} long timeoutTime = r.riceivertime + mTimeoutPeriod;if(timeoutTime > now) {// Reset the broadcast timeout if the currently executing receiver does not timeoutsetBroadcastTimeoutLocked(timeoutTime);
            return;
        }
    }

    BroadcastRecord br = mOrderedBroadcasts.get(0);
    ifState == broadcastRecord.waiting_services) {// BroadcastRecord.waiting_services (broadcastRecord.waiting_services) {// BroadcastRecord.waiting_services (broadcastRecord.waiting_services) { When you wait enough time, the next broadcast is processed. br.curComponent = null; br.state = BroadcastRecord.IDLE; processNextBroadcast(false);
        return;
    }

    r.receiverTime = now;
    //当前BroadcastRecord的anr次数执行加1操作
    r.anrCount++;

    if (r.nextReceiver <= 0) {
        return; }... Object curReceiver = r.receivers.get(r.nextReceiver-1); // Query the App processif (curReceiver instanceof BroadcastFilter) {
        BroadcastFilter bf = (BroadcastFilter)curReceiver;
        if (bf.receiverList.pid != 0
                && bf.receiverList.pid != ActivityManagerService.MY_PID) {
            synchronized (mService.mPidsSelfLocked) {
                app = mService.mPidsSelfLocked.get(
                        bf.receiverList.pid);
            }
        }
    } else {
        app = r.curApp;
    }

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

    if(mPendingBroadcast == r) { mPendingBroadcast = null; } // Continue to move to the next broadcast receiver finishReceiverLocked(R, R.resultcode, R.resultData, R.resultextras, R.resultabort,false);
    scheduleBroadcastsLocked();

    if(anrMessage ! = null) {// [see section 3.3.3] mHandler.post(new AppNotResponding(app, anrMessage)); }}Copy the code
  1. If mOrderedBroadcasts are processed, anR is not displayed.
  2. If dexopt is being executed, anR is not performed.
  3. If the system is not ready (mProcessesReady=false), the system will not anR.
  4. If the currently executing receiver does not timeout, the broadcast timeout is reset without anR.

3.3.3 AppNotResponding

[-> BroadcastQueue.java]

private final class AppNotResponding implements Runnable {
    ...
    public void run() {/ / into the mService ANR processing. AppNotResponding (mApp, null, null,false, mAnnotation); }}Copy the code

Four ContentProvider

ContentProvider Timeout is triggered when ams.mainHandler in the “ActivityManager” thread receives a CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG message.

ContentProvider timeout is CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s. This is completely different from BroadcastQueue. It is related to the start process of the Provider process.

4.1 bomb

This article understands the principle of ContentProvider and introduces the startup process of Provider in detail. AttachApplicationLocked () is called to enter the system_server process.

4.4.1 AMS. AttachApplicationLocked

private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
    ProcessRecord app;
    if(pid ! = MY_PID && pid >= 0) { synchronized (mPidsSelfLocked) { app = mPidsSelfLocked.get(pid); ProcessRecord}}... // if the system is in the ready state or the app is FLAG_PERSISTENTtrueboolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info); List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null; // If the app process has a provider in the process of starting, the CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG message is sent 10 seconds after the timeoutif (providers != null && checkAppInLaunchingProvidersLocked(app)) {
        Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
        msg.obj = app;
        mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
    }

    thread.bindApplication(...);
    ...
}
Copy the code

Detonate the bomb 10 seconds later

4.2 bomb

When the provider successfully publishes, the bomb is defused.

2 AMS. PublishContentProviders

public final void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
   ...

   synchronized (this) {
       final ProcessRecord r = getRecordForAppLocked(caller);

       final int N = providers.size();
       for(int i = 0; i < N; i++) { ContentProviderHolder src = providers.get(i); . ContentProviderRecord dst = r.pubProviders.get(src.info.name);if(dst ! = null) { ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name); mProviderMap.putProviderByClass(comp, dst); // Add the provider to mProviderMap String names[] = dst.info.author.split (";");
               for (int j = 0; j < names.length; j++) {
                   mProviderMap.putProviderByName(names[j], dst);
               }

               int launchingCount = mLaunchingProviders.size();
               int j;
               boolean wasInLaunchingProviders = false;
               for (j = 0; j < launchingCount; j++) {
                   if(mLaunchingProviders. Get (j) = = DST) {/ / remove the provider mLaunchingProviders queue mLaunchingProviders. Remove (j); wasInLaunchingProviders =true; j--; launchingCount--; }} // Successful pubish removes the messageif(wasInLaunchingProviders) { mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r); } synchronized (dst) { dst.provider = src.provider; dst.proc = r; // Wake up the clientwaitWait method dst.notifyall (); }... }}}}Copy the code

4.3 Detonating a Bomb

There is a Handler thread in the system_server process called “ActivityManager”. When the countdown ends, a message is sent to the Handler thread CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG,

4.3.1 MainHandler. HandleMessage

[-> ActivityManagerService.java ::MainHandler]

final class MainHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            caseCONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: { ... ProcessRecord app = (ProcessRecord)msg.obj; Synchronized (ActivityManagerService. This) {/ / (see section 4.3.2 】 processContentProviderPublishTimedOutLocked (app); }}break; . }... }}Copy the code

4.3.2 AMS. ProcessContentProviderPublishTimedOutLocked

Private final void processContentProviderPublishTimedOutLocked ProcessRecord (app) {/ / [see 4.3.3] cleanupAppInLaunchingProvidersLocked(app,true); [see section 4.3.4] removeProcessLocked(app,false.true."timeout publishing content providers");
}
Copy the code

4.3.3 AMS. CleanupAppInLaunchingProvidersLocked

boolean cleanupAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
    boolean restart = false;
    for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
        ContentProviderRecord cpr = mLaunchingProviders.get(i);
        if (cpr.launchingApp == app) {
            if(! alwaysBad && ! app.bad && cpr.hasConnectionOrHandle()) { restart =true;
            } else{/ / remove the provider of death removeDyingProviderLocked (app, CPR,true); }}}return restart;
}
Copy the code

RemoveDyingProviderLocked () function is closely related to the survival of the process: see the ContentProvider reference counting for [] section 4.5]

  • For a provider of stable type (conn.stableCount > 0), it kills all non-persistent processes that establish stable connections with the provider.
  • A provider with unstable core (conn.unstableCount > 0) does not cascade to kill the client process.

4.3.4 AMS. RemoveProcessLocked

private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart, String reason) { final String name = app.processName; final int uid = app.uid; // Remove the corresponding object in mProcessNames removeProcessNameLocked(name, uid);if (mHeavyWeightProcess == app) {
        mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
                mHeavyWeightProcess.userId, 0));
        mHeavyWeightProcess = null;
    }
    boolean needRestart = false;
    if (app.pid > 0 && app.pid != MY_PID) {
        int pid = app.pid;
        synchronized (mPidsSelfLocked) {
            mPidsSelfLocked.remove(pid);
            mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
        }

        ...
        boolean willRestart = false;
        if(app.persistent && ! app.isolated) {if (!callerWillRestart) {
                willRestart = true;
            } else {
                needRestart = true;
            }
        }
        app.kill(reason, true); // Kill handleAppDiedLocked(app, willRestart, allowRestart);if (willRestart) {
            removeLruProcessLocked(app);
            addAppLocked(app.info, false, null /* ABI override */); }}else {
        mRemovedProcesses.add(app);
    }
    return needRestart;
}
Copy the code

Five, the summary

When ANR occurs, ams.appnotresponding () method is called. For details, see the article to understand the information collection process of Android ANR. The exception here is the provider.

The Timeout time

  • For foreground services, the timeout is SERVICE_TIMEOUT = 20s;
  • For background services, the timeout value is SERVICE_BACKGROUND_TIMEOUT = 200s
  • For foreground broadcasts, the timeout is BROADCAST_FG_TIMEOUT = 10s;
  • For background broadcasts, the timeout is BROADCAST_BG_TIMEOUT = 60s.
  • ContentProvider timeout is CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s;

Timeout detection

Service timeout detection mechanism:

  • Anr will be triggered if the corresponding operation is not performed within a certain period of time to trigger the removal delay message.

BroadcastReceiver Timeout detection mechanism

  • If the total execution time of an ordered broadcast exceeds 2* Number of Receivers * timeout period, anR will be triggered.
  • Anr is triggered if the execution of one of the receivers for an ordered broadcast exceeds the timeout period.

In addition:

  • After ANR occurs for Service, Broadcast, and Input, ams.appnotresponding is called.
  • For the provider, in the process of its start ANR the publish process may appear when, can directly kill process and clean up the relevant information, and won’t pop up ANR dialog. AppNotRespondingViaProvider () procedure will go appNotResponding (), I’m not going to talk about this, it’s rarely used, it’s user defined timeout.

The last

If you see this and you think it’s good, give it a thumbs up? If you think there is something worth improving, please leave a message. Will inquire seriously, correct inadequacy. thank you

The reason why someone is better than you is because they are already better and are constantly trying to become better, while you are still satisfied with the status quo and secretly happy! I hope you can click a like and pay attention to me, I will update the technical dry goods in the future, thank you for your support!

Forward + like + attention, the first time to get the latest knowledge

The road to being an Android architect is a long one.