ANR series five: Service type ANR principle explanation

Foreword:

I wrote an article explaining the principle of service-type ANR before, but when I looked back recently, I found that there were many things missing, and the explanation was not clear. The original text was changed too much, so I simply re-wrote it.

There are several articles in the ANR series of articles.

Follow this order to explain one by one:

1. First talk about the basic concept of ANR and the process after ANR occurs;

2. How the four types of ANR occur;

3. How to troubleshoot and solve ANR type problems.

If you want to read the entire series of articles, you can refer to the first article of the series, which will have a clear list:

One of the ANR series: ANR display and log generation principle explanation

This article is the fifth article in the ANR series. This article mainly explains how the ANR type of service type occurs.

The main contents of this article are as follows:

1. ANR introduction of Service type;

2. Introduction to the ANR principle of different reasons under the two Service types;

3. Examples of ANR of six service types, covering all scenarios.

PS: Before reading this article, it is recommended to read the following articles to make a good knowledge reserve to facilitate the understanding of this article.

The second of the four major components of android-service implementation principle analysis

1. Service type ANR introduction

In Android, there are two kinds of service-type ANRs, namely service startup timeout and foreground service timeout, which we will introduce in turn.

1.1 service startup timeout

The target service failed to complete the startup process within the expected time, resulting in an " executing service ... " type of ANR. The service here is not only for background services, but also for foreground services (see Chapter 5 for specific scenarios and explanations).

The corresponding Message type in AMS is declared as follows:

ActivityManagerService.SERVICE_TIMEOUT_MSG

1.2 Foreground service timeout

It refers to when the foreground service is started. At this time, the service needs to complete the setForeground operation within the specified time, otherwise it may prompt " Context.startForegroundService() did not then call Service.startForeground():.. " ANR of type (See Chapter 5 for specific scenarios and explanations).

The corresponding Message type in AMS is declared as follows:

ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG

Below we introduce the two types of ANR in turn. The second chapter introduces the service startup timeout type, and the third chapter introduces the foreground service timeout.

2. Service startup timeout type

2.1 Trigger point of service startup timeout type ANR

First, let's see where the ANR trigger point of the service type is.

As mentioned before, all types of ANR will eventually be notified to the appNotResponding method of the ANRHelper class, and the service type is no exception.

So the final trigger point is in the serviceTimeout method of the ActiveService class:

void serviceTimeout(ProcessRecord proc) {
    ...
    anrMessage = "executing service " + timeout.shortInstanceName;
    ...
    if (anrMessage != null) {
            mAm.mAnrHelper.appNotResponding(proc, anrMessage);
    }
}

And this method is called in ActivityManagerService, and the message type of the corresponding Message is ActivityManagerService.SERVICE_TIMEOUT_MSG.

The relevant code is as follows:

case SERVICE_TIMEOUT_MSG: {
    mServices.serviceTimeout((ProcessRecord)msg.obj);

Let's take a look at when a message of type SERVICE_TIMEOUT_MSG is sent:

// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;

// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    ...
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
            ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

Obviously, a delayed message is sent, that is, a delayed SERVICE_TIMEOUT_MSG type message will be sent during the service startup process. If the time is up and this type of message is not canceled, ANR will occur.

There are two types of delay time configured here, distinguishing whether it belongs to the foreground service or the background service. The timeout period in the foreground is 20S, and in the background is 200S.

There are two types of cancellation, the APP is notified of an exception, or the APP has completed related operations.

 

2.2 When to turn on and turn off timeout detection

After knowing that the system implements the timeout check by registering a delay message of the SERVICE_TIMEOUT_MSG type, let's look at when to register and remove the message of the SERVICE_TIMEOUT_MSG type.

Register for SERVICE_TIMEOUT_MSG type messages

Registering SERVICE_TIMEOUT_MSG is in the scheduleServiceTimeoutLocked method, so let's see when to call this method.

The calls of this method are all in bumpServiceExecutingLocked, let's look at the code:

/**
 * r:service的记录
 * fg:是否由前台应用启动
 * why:启动描述
 */
private boolean bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why,String oomAdjReason) {
    ...
    ProcessServiceRecord psr;
    boolean timeoutNeeded = true;
    if ((mAm.mBootPhase < SystemService.PHASE_THIRD_PARTY_APPS_CAN_START)
        && (r.app != null) && (r.app.getPid() == ActivityManagerService.MY_PID)) {
        timeoutNeeded = false;
    }    
    if (r.executeNesting == 0) {
        r.executeFg = fg;
        if (r.app != null) {
            if (timeoutNeeded && psr.numberOfExecutingServices() == 1) {
                scheduleServiceTimeoutLocked(r.app);
            }
        }
    } else if (r.app != null && fg) {
        if (!psr.shouldExecServicesFg()) {
            if (timeoutNeeded) {
                scheduleServiceTimeoutLocked(r.app);
            }
        }
    }
    ...
    r.executeNesting++;
    r.executingStart = SystemClock.uptimeMillis();
}

The explanation is as follows:

1. If the startup is initiated by SystemService, if the timing is too early, there is no need to do timeout detection. At this time, the system has not yet been initialized, so it is easy to cause ANR due to system-side reasons.

2. If there is no task in progress, and there is only one service being started in the foreground, call scheduleServiceTimeoutLocked to enable timeout detection.

3. If the service is started by the foreground application, timeout detection also needs to be enabled.

To summarize here, it means that when the service is started, when the first life cycle task is executed, or when the foreground application is started, timeout detection will be enabled.

Remove SERVICE_TIMEOUT_MSG type messages

Removing the SERVICE_TIMEOUT_MSG type message is in the serviceDoneExecutingLocked method, and the relevant code is as follows:

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
        boolean finishing, boolean enqueueOomAdj) {
    r.executeNesting--;
    if (r.executeNesting <= 0) {
        if (r.app != null) {
            if (psr.numberOfExecutingServices() == 0) {
                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
            }
            ..
        }
        ...
        r.executeFg = false;
    }
}

The explanation is as follows:

1. Every time the task is executed, the serviceDoneExecutingLocked method will be called, and the number of life cycle methods will be -1.

2. When all lifecycle methods are executed and there is no service startup task that has not yet been executed, the SERVICE_TIMEOUT_MSG type message is removed. That is to say, it is only at this time that the entire service startup process is considered to be over.

2.3 AMS enable timeout detection

As mentioned in the previous article on the principle of service startup process, there are two processes in the service startup process:

1. Notify the APP to create a Service. The corresponding method is: realStartServiceLocked.

2. Notify the APP to execute the Service life cycle, the corresponding method is mainly: sendServiceArgsLocked.

Let's look at the first realStartServiceLocked method first:

private final void realStartServiceLocked(...) throws RemoteException {
    //启动超时检测
    bumpServiceExecutingLocked(r, execInFg, "create");
    ...
    //通知APP去创建service
    try {
        app.thread.scheduleCreateService(..);
        created = true;
    }catch(Exception e){
    
    }finally{
        if (!created) {
            ...
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);
        }
    }
    ...
    //通知APP进行service的生命周期
    sendServiceArgsLocked(r, execInFg, true);
}

The core logic of the method is summarized as follows:

1. At the beginning of the method, the timeout detection will be enabled through the bumpServiceExecutingLocked method.

2. Then notify the APP to create a service.

3. If there is an exception in the process created by the notification, end the timeout detection through the bumpServiceExecutingLocked method.

4. After the notification is created, naturally continue to notify the APP to execute the service life cycle process.

Let's take a look again, the sendServiceArgsLocked method:

private final void sendServiceArgsLocked(...){
    while (r.pendingStarts.size() > 0) {
        //获取启动周期所对应的事务对象
        ServiceRecord.StartItem si = r.pendingStarts.remove(0);
        //加入集合
        r.deliveredStarts.add(si);
        ...
        //开启生命周期超时检测
        bumpServiceExecutingLocked(r, execInFg, "start", null /* oomAdjReason */);
        ...
        //构造启动参数
        args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent));
    }
    ...
    ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
    try {
        //通知APP执行对应生命周期
        r.app.getThread().scheduleServiceArgs(r, slice);
    } catch(Exception e){
        //通知流程出现异常
        caughtException = e;
    }
    //出现异常,则结束流程
    if(caughtException != null){
        for (int i = 0, size = args.size(); i < size; i++) {
            serviceDoneExecutingLocked(r, inDestroying, inDestroying, true);
        }
    }
}

The core logic of the method is summarized as follows:

1. At the beginning of the method, obtain the life cycle items contained in the ServiceRecord object and add them to the deliveredStarts collection.

2. Build as many timeout detections as there are life cycle startup functions.

3. Notify the APP to execute the corresponding lifecycle method.

4. If the notification fails, end those timeout checks just created.

2.4 APP processing flow

We still divide it into two scenarios to look at the processing flow of APP.

The first type: service creation;

The second type: service execution life cycle.

Execute the service creation process on the APP side

The article on the principle of service implementation has introduced that in 2.3 above, the APP is notified to execute the process of creating a service. The scheduleCreateService method in the ApplicationThread object receives the creation notification on the APP side, and finally it is handed over to the handleCreateService method in the ActivityThread. Let’s take a look at the relevant code.

private void handleCreateService(CreateServiceData data) {
    
    //完成创建service
    service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);

    //调用service的attach方法
    service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());

    //调用service的onCreate方法
    service.onCreate();

    //通知回系统侧
    ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}

We can see that the serviceDoneExecuting method in AMS is notified through the serviceDoneExecuting method. Next, we will look at the serviceDoneExecuting method in AMS and the serviceDoneExecutingLocked method in ActiveServices.

public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
    synchronized(this) {
        ...
        mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false);
    }
}
//ActiveServices
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,
        boolean enqueueOomAdj) {
    if (r != null) {
        ...
        //serviceDoneExecutingLocked方法就是2.2种介绍的关闭超时检测的方法
        serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj);
    }
}

It can be seen that in the end, the APP side notifies the system that the task has been completed through the binder method serviceDoneExecuting. When all startup tasks of the service are completed, the system stops the timeout detection process.

Execute the service execution life cycle process on the APP side

The article on the principle of service implementation has introduced that in 2.3 above, the APP is notified to execute the life cycle process of the service. It is the scheduleServiceArgs method in the ApplicationThread object that receives the creation notification on the APP side, and it is finally handed over to the handleServiceArgs method in the ActivityThread. Let’s take a look. Relevant code:

private void handleServiceArgs(ServiceArgsData data) {
    Service s = mServices.get(data.token);
    if(s != null ){
        if (!data.taskRemoved) {
            res = s.onStartCommand(data.args, data.flags, data.startId);
        } else {
            s.onTaskRemoved(data.args);
            res = Service.START_TASK_REMOVED_COMPLETE;
        }
        ... 
        ActivityManager.getService().serviceDoneExecuting(
            data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
        ...
    }
}

It can be seen that the onStartCommand method of the Service will be executed first, and then the binder method serviceDoneExecuting will be used to notify the system that the related tasks have been completed.

2.5 Scenarios when the APP process does not exist

The two scenarios described above are all when the process exists. What if the Service process does not exist? For the whole process, you can refer to the Service principle introduced at the beginning to explain this article.

Draw a flow chart here for easy explanation.

 

After the process is created, the AMS is notified to bind. In attachApplication, thread.bindApplication and realStartServiceLocked are called successively to notify the APP to initialize the process and notify the APP to start the Service respectively.

On the APP side, the relevant processes are all in the main thread, so if bindApplication takes a lot of time, it will cause handleCreateService and handleServiceArgs to fail to execute on time, resulting in the eventual ANR.

In fact, if the total time of the three methods of Application.onCreate(), Service.onCreate(), and Service.onStartCommand() exceeds the specified timeout time, ANR will be generated.

2.6 Summary

Here we make a small summary, summarizing the situation of ANR caused by Service startup timeout.

There are two scenarios in ActiveServices that will trigger the registration timeout check. After registration, the APP is notified to complete the relevant operations. If the APP completes all tasks according to the instructions, the timeout check task will be cancelled. If there are still outstanding tasks after the timeout expires, an ANR will occur.

The overall flow chart is as follows:

 

3. Foreground service timeout type

Similar to the process in Chapter 2, let's first see when the delayed message of the foreground service type will be registered.

3.1 Introduction to Foreground Service Timeout Type ANR

First, let's look at when a message of type ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG is registered. The related method is in scheduleServiceForegroundTransitionTimeoutLocked, as follows:

private static final int DEFAULT_SERVICE_START_FOREGROUND_TIMEOUT_MS = 30 * 1000;
volatile int mServiceStartForegroundTimeoutMs = DEFAULT_SERVICE_START_FOREGROUND_TIMEOUT_MS;

void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
    Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG);
    mAm.mHandler.sendMessageDelayed(msg, mAm.mConstants.mServiceStartForegroundTimeoutMs);
}

The default timeout is 30S, which can be modified by configuration.

3.2 When to turn on and turn off timeout detection

when to open

Let's first look at when to turn on detection. Let's take a look at when to call this method. There is only one scenario where it is called, which is in the sendServiceArgsLocked method.

private final void sendServiceArgsLocked(...){
    ...
    while (r.pendingStarts.size() > 0) {
        ...
        if (r.fgRequired && !r.fgWaiting) {
            if (!r.isForeground) {
                scheduleServiceForegroundTransitionTimeoutLocked(r);
            }else{
                r.fgRequired = false;
            }
        }
    }
}

As mentioned before, sendServiceArgsLocked is called when executing the life cycle.

Let’s look at the judgment conditions again: r.fgRequired && !r.fgWaiting means that the Service requires the foreground display, and the timeout waiting process has not yet started.

Next is the judgment condition: !r.isForeground, which represents whether it is currently in the foreground.

To sum up, when the Service requires the foreground to display, and has not started the timeout waiting process, and is not in the foreground, the timeout detection will start.

when will it end

There are 3 calls to remove the ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG type message, let's look at it in turn.

First place:

private void bringDownServiceLocked(ServiceRecord r, boolean enqueueOomAdj) {
    if (r.fgRequired) {
        mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
        mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
        if (r.app != null) {
            Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
            ...
            mAm.mHandler.sendMessage(msg);
        }
    }
}

bringDownServiceLocked represents the end of the process of starting the Service. When the process ends, the registration of the timeout message will naturally be removed.

Second place:

void performScheduleRestartLocked(ServiceRecord r, ...) {
    if (r.fgRequired && r.fgWaiting) {
        mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
        r.fgWaiting = false;
    }
}

performScheduleRestartLocked means restarting the process of starting the Service. At this time, it is natural to remove the registration of the timeout message.

Third place:

private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
        Notification notification, int flags, int foregroundServiceType) {
    if (id != 0) {
        if (r.fgRequired) {
            mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
        }
    }
}

The third place is the main process call we need. When the setServiceForegroundInnerLocked method is called, the timeout detection of the SERVICE_FOREGROUND_TIMEOUT_MSG type will be stopped.

So next, I will see how the APP notifies the system to execute this method.

3.3 APP side processing flow

When the foreground Service starts, the onCreate and onStartCommand methods are called respectively. The timing to start the detection above is to call the life cycle method onStartCommand method.

Therefore, within 20 seconds after the onStartCommand method, we need to call the startForeground method to bind the foreground notification. At this time, the binder method will be used to notify the setServiceForeground() method of AMS, thereby ending the timeout process of the foreground service type and avoiding ANR.

The relevant calling process is as follows:

 

3.4 The process after the foreground service times out

This is different from the service startup timeout type. Even if the SERVICE_FOREGROUND_TIMEOUT_MSG type message eventually times out and causes this message to be executed, the ANR box will not pop up immediately. Let's take a look at the method serviceForegroundTimeout executed after the timeout:

//ActivityManagerConstants类
final class ActivityManagerConstants{
    private static final int DEFAULT_SERVICE_START_FOREGROUND_ANR_DELAY_MS = 10 * 1000;
    volatile int mServiceStartForegroundAnrDelayMs = DEFAULT_SERVICE_START_FOREGROUND_ANR_DELAY_MS;
}
//ActiveServices类
public final class ActiveServices{
    //SERVICE_FOREGROUND_TIMEOUT_MSG类型消息触发后执行该方法
    void serviceForegroundTimeout(ServiceRecord r) {
        ProcessRecord app;
        synchronized (mAm) {
            if (!r.fgRequired || !r.fgWaiting || r.destroying) {
                return;
            }
            app = r.app;
            ...
            r.fgWaiting = false;
            stopServiceLocked(r, false);
        }
        if (app != null) {
            final String annotation = "Context.startForegroundService() did not then call "+ "Service.startForeground(): " + r;
            Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_ANR_MSG);
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = app;
            args.arg2 = annotation;
            msg.obj = args;
            mAm.mHandler.sendMessageDelayed(msg,mAm.mConstants.mServiceStartForegroundAnrDelayMs);
        }
    }
    
    //SERVICE_FOREGROUND_TIMEOUT_ANR_MSG类型消息触发后执行该方法
    void serviceForegroundTimeoutANR(ProcessRecord app, String annotation) {
        mAm.mAnrHelper.appNotResponding(app, annotation);
    }
}

We can see that it is mainly divided into two processes:

1. Execute the process related to stopServiceLocked;

2. Execute the SERVICE_FOREGROUND_TIMEOUT_ANR_MSG type message after a 10S delay. And when this message is triggered, the ANR process is actually executed.

The process after stopServiceLocked is very complicated. For the convenience of readers, I organized it into the flow chart shown below:

 

Related explanation:

  1. After stopServiceLocked is executed, it will pass a series of method calls, and finally send a message to the application side through the binder method, informing it that a timeout exception has occurred.

  2. After the binder thread of the application receives the notification, there are two scenarios at this time, the main thread is blocked (3) and the main thread is not blocked (4-7).

  3. If the main thread is blocked and reaches more than 10S, the application will not notify the system of a crash. Therefore, after 10S, the ANR process will be executed normally, and the ANR box will pop up.

  4. If the main thread is not blocked, the final application will go to the crash process and notify the system that the application crash has occurred. Crash process can refer to another article: android source code learning - android exception handling mechanism

  5. After the system receives the message that the APP has an exception, it will execute the exception process, record the relevant exception log, and finally end the APP process through the ShellCommand command

  6. After the APP process is killed, the system will receive relevant notifications, and then perform state synchronization on the relevant member variables in the binding object ProcessRecord of the process in the system. Among them, the following settings will be made:

    mPid = 0
    mKilled = true;
  7. After 10 seconds, execute the serviceForegroundTimeoutANR process. When entering the ANR process, it will judge whether the pid is 0. If the pid is equal to 0 at this time, the subsequent ANR process will not be executed.

    void appNotResponding(...){
        final int incomingPid = anrProcess.mPid;
        if (incomingPid == 0) {
            Slog.i(TAG, "Skip zero pid ANR, process=" + anrProcess.processName);
            return;
        }
        ...
    }

Therefore, if the application process is not blocked, the system will record an abnormal crash log.

3.5 Summary

Here is also a summary, that is to say, when a Service of the foreground service type is started, within 30 seconds after the Service is started, the APP needs to actively call the startForeground() method to bind the relevant foreground notifications, otherwise once the timeout expires, it will Enter the timeout process.

After entering the timeout process, check whether the application process is blocked to decide whether to go through the ANR non-responsive process or the Crash abnormal process.

The relevant flow chart is as follows:

 

4. Instance Description of Service Type ANR

According to the above explanation, we know that there are two types of ANR of Service type, namely service startup timeout and foreground service unbound timeout.

Next, we will give a few examples to illustrate Service timeouts in various scenarios.

Example 1: Time-consuming operation in onCreate of service

code show as below:

public class ThreadService extends IntentService {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("ThreadService", "ThreadService onCreate");
        try {
            Thread.sleep(60_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Then start it with code:

 val intent = Intent(this, ThreadService::class.java)
 startForegroundService(intent)

The startService method is used here instead of startService. If it starts in the background, it needs to sleep for more than 200S. The final experiment results in an " executing service ... " type of ANR.

Example 2: Time-consuming operation in onStartCommand of service

code show as below:

public class ThreadService extends IntentService {
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.i("ThreadService", "onStartCommand");
        try {
            Thread.sleep(60_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return super.onStartCommand(intent, flags, startId);
    }
}

Start the service in the same way as in Example 1, and the final experiment results in an ANR of the type " executing service ... ".

Example 3: Time-consuming operation in application

Add time-consuming operations in Application and remove time-consuming operations in Service.

public class DemoApplication extends Application {

    public void onCreate() {
        super.onCreate();
        try {
            Thread.sleep(25_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Start the service in the same way as in Example 1, and the final experiment results in an ANR of the type " executing service ... ".

Example 4: Both Application.onCreate/Service.onCreate and Service.onStartCommand have time-consuming operations

The relevant code is as follows:

public class DemoApplication extends Application {

    public void onCreate() {
        super.onCreate();
        try {
            Thread.sleep(8_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadService extends IntentService {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("ThreadService", "ThreadService onCreate");
        try {
            Thread.sleep(8_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.i("ThreadService", "onStartCommand");
        try {
            Thread.sleep(8_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return super.onStartCommand(intent, flags, startId);
    }
}

A total of three sleep operations have been performed, each time 8S. The timeout period of the foreground service is 20S, and ANR will only occur if the time is accumulated three times.

Start the service in the same way as in Example 1, and the final experiment results in an ANR of the type " executing service ... ".

Therefore, it means that the accumulated time of three times exceeds the set timeout period, and ANR will be generated.

Of course, this conclusion is not absolute, because the judgment condition for judging whether to remove the timeout check in the system is whether the currently unexecuted task is 0, so if the execution speed in application and onCreate is fast, and a freeze occurs in system_server, this Sometimes the onCreate task has been executed, but the life cycle method has not been registered. At this time, the unexecuted task is 0, so the timeout task will be removed and the calculation will start from 0.

Example 5: The foreground service is not bound and the main thread is blocked

Because the time for the front desk overtime service is 30S, we delay the main thread for 29 seconds, so that the abnormal information passed by the system will be blocked and cannot be processed. We just block it for 15S, because as mentioned above, the system is for the front desk. The waiting time for the service type is only 20S.

code show as below:

public class ThreadService extends IntentService {
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.i("ThreadService", "onStartCommand");
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(15_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 29_000);
        return super.onStartCommand(intent, flags, startId);
    }
}

Start the service in the same way as in Example 1, and eventually ANR is generated. The relevant logs are as follows:

2023-03-31 11:35:30.459  1736-1968  ActivityManager         pid-1736                             W  Bringing down service while still waiting for start foreground: ServiceRecord{99501ef u0 com.xt.client/.service.ThreadService}
...
2023-03-31 11:35:40.593  1736-26437 ActivityManager         pid-1736                             E  ANR in com.xt.client
                                                                                                    PID: 26405
                                                                                                    Reason: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{99501ef u0 com.xt.client/.service.ThreadService}
                                                                                                    ErrorId: 3b070281-4717-4b64-83a3-ec41a48f5e9a

Example 6: The foreground service is not bound and the main thread is not blocked

code show as below:

public class ThreadService extends IntentService {
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.i("ThreadService", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
}

Start the service in the same way as in example 1. In the end, the ANR box does not pop up, but a crash occurs. The relevant logs are as follows. This process is exactly the same as our analysis in 3.4.

//进程创建
2023-03-30 17:31:59.002 26981-26981 lxltest                 com.xt.client                        I  DemoApplication init
//Service完成创建
2023-03-30 17:31:59.026 26981-26981 ThreadService           com.xt.client                        I  ThreadService onCreate
2023-03-30 17:31:59.027 26981-26981 ThreadService           com.xt.client                        I  onStartCommand
//APP发生异常,VM虚拟机关闭,并且通知系统发生异常
2023-03-30 17:32:28.717 26981-26981 AndroidRuntime          com.xt.client                        D  Shutting down VM
2023-03-30 17:32:28.720 26981-26981 AndroidRuntime          com.xt.client                        E  FATAL EXCEPTION: main
                                                                                                    Process: com.xt.client, PID: 26981
//发出进程退出信号
2023-03-30 17:45:25.142 27448-27448 Process                 com.xt.client                        I  Sending signal. PID: 27448 SIG: 9
//进程被杀死,收到回调通知,进行清理操作
2023-03-30 17:45:25.172  1736-1981  ActivityManager         pid-1736                             I  Process com.xt.client (pid 27448) has died: cch+5 CEM
2023-03-30 17:45:25.178  1736-2014  libprocessgroup         pid-1736                             I  Successfully killed process cgroup uid 10235 pid 27448 in 5ms
//因为pid为0,所以跳过ANR流程,没有弹出ANR的提示框。
2023-03-30 17:45:35.128  1736-1968  ActivityManager         pid-1736                             I  Skip zero pid ANR, process=com.xt.client

5. Summary

So let's summarize the ANR types of service, there are two types:

The first type: executing service ... type.

Application creation time + Service.onCreate time + Service.onStartCommand time is greater than the timeout time (foreground service 20S, background service 200S).

The second type: Context.startForegroundService() .. type

This type only takes effect for foreground services. After starting the foreground service, the notification binding needs to be completed within 30 seconds. If the time is up and the binding fails, and the main thread of the application is blocked for more than 10 seconds, this type of ANR will occur.

Guess you like

Origin blog.csdn.net/AA5279AA/article/details/129878691