review

Activity start series summary:

Summary of Activity startup process -ActivityThread

Summary of the Activity startup process – Lifecycle

Summary of Activity startup process – Timeout determination mechanism

In the Activity startup process, the interaction between the APP process and ActivityManagerService and the initialization process of the application process are briefly summarized. As one of the four components, the startup process of a Service is broadly similar.

An overview of the

In development we use context.startService(intent) and context.stopService(intent) to start and stop a Service. Service starts and stops are also scheduled by ActivityManagerService, and this article traces the source code to see how it starts and stops.

The source code to explore

The source code based on Android 10.0

StartService process

Context. StartService (intent) : [ContextImpl#startService]

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

public ComponentName startForegroundService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, true, mUser);
}

private ComponentName startServiceCommon(Intent service, boolean requireForeground,
        UserHandle user) {
    try {
        / /...
        // request AMS to start the Service
        ComponentName cn = ActivityManager.getService().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), requireForeground,
                        getOpPackageName(), user.getIdentifier());
        if(cn ! =null) {
            // omit check for exceptions
        }
        return cn;
    } catch (RemoteException e) {
        throwe.rethrowFromSystemServer(); }}Copy the code

You can see that ActivityManagerService is called to startService through IActivityManager.

Next from the APP process to ActivityManagerService:

[ActivityManagerService#startService]

public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, boolean requireForeground, String callingPackage, int userId)
        throws TransactionTooLargeException {
    enforceNotIsolatedCaller("startService");
    // Refuse possible leaked file descriptors
    // omit parameter exception check
    synchronized(this) {
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        ComponentName res;
        try {
            // mServices instance is ActiveServices
            res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid,
                    requireForeground, callingPackage, userId);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
        returnres; }}Copy the code

This method calls ActiveServices’ startServiceLocked method for further processing.

ActiveServices’ startServiceLocked method calls another overloaded method: [ActiveServices#startServiceLocked]

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, boolean fgRequired, String callingPackage,
        final int userId, boolean allowBackgroundActivityStarts)
        throws TransactionTooLargeException {
    // omit the part of the variable callerFg (used to mark whether the caller process is foreground) ···
    
    // Find or create an available ServiceRecord
    ServiceLookupResult res =
        retrieveServiceLocked(service, null, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg, false.false);
    // omit the part that checks the RES results
    
    ServiceRecord r = res.record;
    
    // Omit background boot check and boot permission check part
    
    // Create the startCommand parameter and add it to the pendingStarts collection
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
        service, neededGrants, callingUid));
    
    // Call startServiceInnerLocked for further processing
    ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    return cmp;
Copy the code

Here we get a ServiceRecord (wrapped in ServiceLookupResult). The ServiceRecord is used to hold Service information. Each ServiceRecord corresponds to a running Service. The startServiceInnerLocked method is then called to further the startup process.

The retrieveServiceLocked method first retrieves the Set of ServiceMap cache corresponding to the userId and finds the ServiceRecord based on either ComponentName (explicit intent) or IntentFilter (implicit intent). If not, it looks for the matching ServiceInfo from PackageManagerService, then creates the ServiceRecord and saves it in the collection. It also determines whether to allow calls and startup permission checks between different applications and userids before returning results.

Next enter the startServiceInnerLocked method:

[ActiveServices#startServiceInnerLocked]

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
        boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    / /...
    // Perform further startup
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false.false);
    if(error ! =null) {
        return new ComponentName("!!!!!", error);
    }
    
    // Omit add background start service set and start foreground service judgment part
    
    return r.name;
}
Copy the code

Enter the bringUpServiceLocked method:

[ActiveServices#bringUpServiceLocked]

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting, boolean permissionsReviewRequired)
        throws TransactionTooLargeException {
    // Check whether the target Service is started for the first time. The IApplicationThread is not set in ServiceRecord
    if(r.app ! =null&& r.app.thread ! =null) {
        // The scheduling application process triggers the onStartCommand callback
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }
    
    // Determine whether the target Service is waiting to restart
    if(! whileRestarting && mRestartingServices.contains(r)) {// If waiting for a restart, then do nothing.
        return null;
    }
    
    / /...
    
    // Reset the delayed start flag of the target Service
    // Check whether the user corresponding to the userId of the target Service is started
    / /...
    
    final booleanisolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) ! =0;
    final String procName = r.processName;
    HostingRecord hostingRecord = new HostingRecord("service", r.instanceName);
    ProcessRecord app;

    if(! isolated) {// Find ProcessRecord for the corresponding process
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                    + " app=" + app);
        // Determine whether the process is started by IApplicationThread in ProcessRecord
        if(app ! =null&& app.thread ! =null) {
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                // Perform further startup
                realStartServiceLocked(r, app, execInFg);
                return null;
            } catch (TransactionTooLargeException e) {
                throw e;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.}}else {
        // Omit ISOLATED PROCESS SERVICE
    }

    // Not running -- get it started, and enqueue this service record
    // to be executed when the app comes up.
    if (app == null && !permissionsReviewRequired) {
        // Omits the start process part, the mam.startProcessLocked method is called
    }
    
    / /...
    
    // Add ServiceRecord to the cache of the collection to be started and wait for the startup process to start
    if(! mPendingServices.contains(r)) { mPendingServices.add(r); }// If the Service is asked to stop, the stopServiceLocked method is called
    
    return null;
}
Copy the code

This method determines whether the target Service is started, and if so, the onStartCommand callback is scheduled. If yes, perform the Service startup process. Otherwise, add ServiceRecord to the set to be started and wait for the target process to complete.

ProcessRecord holds the IApplicationThread to determine whether the target Service and process are started. When the target Service starts, ProcessRecord and IApplicationThread are set for the corresponding ServiceRecord. When the target process starts, the IApplicationThread is set for the corresponding ProcessRecord. May refer toActivity Startup Process Summary -ActivityThread.

Next, enter the key method realStartServiceLocked:

[ActiveServices#realStartServiceLocked]

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    / /...
    // ProcessRecord is set to ServiceRecord to determine whether the target Service is started
    r.setProcess(app);
    / /...
    // The Services collection of ProcessRecord holds information about all the services running in the corresponding process
    final boolean newService = app.services.add(r);
    // The timeout setting is relevant
    bumpServiceExecutingLocked(r, execInFg, "create");
    / /...
    
    boolean created = false;
    try {
        // Schedule the application process to start the Service
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                app.getReportedProcState());
        // foreground Service The foreground notification is related
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        Slog.w(TAG, "Application dead when creating service " + r);
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        if(! created) {// Omit the exception handling part of Service startup failure}}/ /...
    // omit the process of bind Service
    / /...
    
    // Sending parameters to the application triggers the onStartCommand callback
    sendServiceArgsLocked(r, execInFg, true);
    
    / /...
}
Copy the code

Here, the application process will be scheduled twice. The application side will add two task messages to the message queue of the main application process. The AMS side will continue to execute the rest of its logic.

Before looking at the create Service process, take a look at the sendServiceArgsLocked method: [ActiveServices#sendServiceArgsLocked]

private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
        boolean oomAdjusted) throws TransactionTooLargeException {
    PendingStarts saves the startCommand parameters to be sent
    final int N = r.pendingStarts.size();
    if (N == 0) {
        return;
    }

    ArrayList<ServiceStartArgs> args = new ArrayList<>();
    
    while (r.pendingStarts.size() > 0) {
        // The encapsulation parameters are stored in args
    }

    // ParceledListSlice is used to transfer a large number of Parcelable objects across IPC, which can be split into multiple binder_transact transaction transfers.
    ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
    slice.setInlineCountLimit(4);
    Exception caughtException = null;
    try {
        // Schedule the application process to execute the onStartCommand callback
        r.app.thread.scheduleServiceArgs(r, slice);
    } catch (TransactionTooLargeException e) {
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
                + " args, first: " + args.get(0).args);
        Slog.w(TAG, "Failed delivering service starts", e);
        caughtException = e;
    } catch (RemoteException e) {
        // Remote process gone... we'll let the normal cleanup take care of this.
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
        Slog.w(TAG, "Failed delivering service starts", e);
        caughtException = e;
    } catch (Exception e) {
        Slog.w(TAG, "Unexpected exception", e);
        caughtException = e;
    }
    
    if(caughtException ! =null) {
        // Keep nesting count correct
        // omit the exception handling part}}Copy the code

In this method, the batch StartCommand parameter is encapsulated and the application process is scheduled to execute the scheduleServiceArgs method.

The create and start message tasks of the Service will be executed on the application side.

OnCreate phase

App. Thread. ScheduleCreateService ActivityThread scheduleCreateService method will execute the application process, the method will happen in the H.C REATE_SERVICE message to the main thread, The handleCreateService method will be executed.

[ActivityThread#handleCreateService]

private void handleCreateService(CreateServiceData data) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    // Remove the message requesting GC
    unscheduleGcIdler();

    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        // Instantiate the target Service through reflection
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
    } catch (Exception e) {
        // omit exception handling
    }

    try {
        if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

        // Create context ContextImpl
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        Context holds the service instance
        context.setOuterContext(service);

        // Get the Application instance
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        // The service holds the passed parameter. The attachBaseContext method is called to make mBase hold the ContextImpl
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        // Call the onCreate lifecycle callback method of the Service
        service.onCreate();
        // Add a service instance to the collection cache with a token (token is a ServiceRecord client proxy from AMS, ServiceRecord inherits from Binder)
        mServices.put(data.token, service);
        try {
            // Notify AMS that creation is complete
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
        } catch (RemoteException e) {
            throwe.rethrowFromSystemServer(); }}catch (Exception e) {
        // omit exception handling}}Copy the code

The target Service is first created by reflection, then ContextImpl is created and ContextImpl and Service hold references to each other, and the Application instance is retrieved and the Service holds references to it. The onCreate lifecycle callback method is then called. Finally, notify AMS that the creation is complete.

Again came to ActivityManagerService, in its serviceDoneExecuting method again call ActiveServices serviceDoneExecutingLocked method: [ActiveServices#serviceDoneExecutingLocked]

void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
    // mDestroyingServices saves the Service that performs the destruction
    boolean inDestroying = mDestroyingServices.contains(r);
    if(r ! =null) {
        // Determine the type.
        // The initial create is SERVICE_DONE_EXECUTING_ANON, the startCommand is SERVICE_DONE_EXECUTING_START, and the stop is SERVICE_DONE_EXECUTING_STOP.
        if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
            // Set parameters related to ServiceRecord, including stopIfKilled. ···
        } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) {
            / /...
        }
        final long origId = Binder.clearCallingIdentity();
        // Update the status in ServiceRecord to remove the timeout message
        serviceDoneExecutingLocked(r, inDestroying, inDestroying);
        Binder.restoreCallingIdentity(origId);
    } else {
        Slog.w(TAG, "Done executing unknown service from pid "
                + Binder.getCallingPid());
    }
Copy the code

This method handles parameter and status update Settings in ServiceRecord and removes timeout messages.

OnStartCommand phase

App. Thread. ScheduleServiceArgs will perform ActivityThread scheduleServiceArgs method: [ActivityThread# scheduleServiceArgs]

public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
    List<ServiceStartArgs> list = args.getList();

    for (int i = 0; i < list.size(); i++) {
        // Take the parameters and send them to the main thread for execution
        ServiceStartArgs ssa = list.get(i);
        ServiceArgsData s = newServiceArgsData(); s.token = token; s.taskRemoved = ssa.taskRemoved; s.startId = ssa.startId; s.flags = ssa.flags; s.args = ssa.args; sendMessage(H.SERVICE_ARGS, s); }}Copy the code

The h.service_args message will execute the handleServiceArgs method: [ActivityThread#handleServiceArgs]

private void handleServiceArgs(ServiceArgsData data) {
    // Retrieve the Service instance created and cached in the CREATE phase
    Service s = mServices.get(data.token);
    if(s ! =null) {
        try {
            if(data.args ! =null) {
                // Sets the ClassLoader for the Intent
                data.args.setExtrasClassLoader(s.getClassLoader());
                data.args.prepareToEnterProcess();
            }
            int res;
            if(! data.taskRemoved) {// Triggers the onStartCommand lifecycle callback for the Service
                res = s.onStartCommand(data.args, data.flags, data.startId);
            } else {
                s.onTaskRemoved(data.args);
                res = Service.START_TASK_REMOVED_COMPLETE;
            }

            // This blocks the current thread until the Runnable in QueuedWork completes. (SharedPreference will use)
            QueuedWork.waitToFinish();

            try {
                // Notify AMS that execution is complete
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
            } catch (RemoteException e) {
                throwe.rethrowFromSystemServer(); }}catch (Exception e) {
            if(! mInstrumentation.onException(s, e)) {throw new RuntimeException(
                        "Unable to start service " + s
                        + " with " + data.args + ":"+ e.toString(), e); }}}}Copy the code

The cached Service instance is fetched and its onStartCommand lifecycle callback method is invoked. AMS will also be notified of the status update operation.

At this point, the Service startup process is complete, after the onCreate and onStartCommand lifecycle callbacks.

Service Timeout mechanism

During Service startup, AMS performs Settings related to the timeout mechanism, including setting the timeout duration, triggering the timeout processing, and removing the timeout processing.

Set the timeout

Back to ActiveServices# realStartServiceLocked method, before scheduling application process to create the Service, will first perform bumpServiceExecutingLocked method, In this method will be called timeout scheduleServiceTimeoutLocked method to monitor Settings.

[ActiveServices#scheduleServiceTimeoutLocked]

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return;
    }
    // Get the SERVICE_TIMEOUT_MSG message
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    // Send a delay message
    mAm.mHandler.sendMessageDelayed(msg,
            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
Copy the code

This method sends a delay message SERVICE_TIMEOUT_MSG, which will trigger a timeout if it is not completed after 20 seconds (starting the foreground service) or 200 seconds (starting the background service).

Remove the timeout

When the application process side after completion of the Service start will invoke the AMS side ActiveServices# serviceDoneExecutingLocked method, which will remove the timeout messages: [ActiveServices#serviceDoneExecutingLocked]

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
        boolean finishing) {
    / /...
    // Remove SERVICE_TIMEOUT_MSG message
    mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
    / /...
}
Copy the code
Trigger a timeout

If the startup process and AMS are not notified within the specified time, the timeout logic will be triggered and the serviceTimeout method of ActiveServices will be executed.

[ActiveServices#serviceTimeout]

void serviceTimeout(ProcessRecord proc) {
    / /...
    if(anrMessage ! =null) {
        // Dump ANR information and trigger ANR pop-ups
        proc.appNotResponding(null.null.null.null.false, anrMessage); }}Copy the code

When triggering timeout, ANR will be triggered.

When the target Service process is not started

In the previous ActiveServices#bringUpServiceLocked method, the application process is started if the target Service process is not already started, and the ServiceRecord is saved in the mPendingServices collection.

When the application process is started, the attachApplicationLocked method of ActivityManagerService is called, in which the services to be started are executed.

[ActivityManagerService#attachApplicationLocked]

private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    / /...
    // thread.bindApplication
    / /...
    // Find any services that should be running in this process...
    if(! badApp) {try {
            // Start the Service waiting for the process to start
            didSomething |= mServices.attachApplicationLocked(app, processName);
            checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
        } catch (Exception e) {
            Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
            badApp = true; }}/ /...
}
Copy the code

ActiveServices attachApplicationLocked called [ActiveServices#attachApplicationLocked]

boolean attachApplicationLocked(ProcessRecord proc, String processName)
        throws RemoteException {
    boolean didSomething = false;
    // Collect any services that are waiting for this process to come up.
    if (mPendingServices.size() > 0) {
        ServiceRecord sr = null;
        try {
            // Iterate over the ServiceRecord to be started
            for (int i=0; i<mPendingServices.size(); i++) {
                sr = mPendingServices.get(i);
                if(proc ! = sr.isolatedProc && (proc.uid ! = sr.appInfo.uid || ! processName.equals(sr.processName))) {continue;
                }

                mPendingServices.remove(i);
                i--;
                proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode,
                        mAm.mProcessStats);
                // Execute realStartServiceLocked to start the process
                realStartServiceLocked(sr, proc, sr.createdFromFg);
                didSomething = true;
                if(! isServiceNeededLocked(sr,false.false)) {
                    // We were waiting for this service to start, but it is actually no
                    // longer needed. This could happen because bringDownServiceIfNeeded
                    // won't bring down a service that is pending... so now the pending
                    // is done, so let's drop it.bringDownServiceLocked(sr); }}}catch (RemoteException e) {
            Slog.w(TAG, "Exception in new application when starting service "
                    + sr.shortInstanceName, e);
            throwe; }}// Also, if there are any services that are waiting to restart and
    // would run in this process, now is a good time to start them. It would
    // be weird to bring up the process but arbitrarily not let the services
    // run at this point just because their restart time hasn't come up.
    if (mRestartingServices.size() > 0) {
        // Omit the part about the Service to be restarted
    }
    return didSomething;
}
Copy the code

As you can see, after AMS is notified that the application process has been started, the Service startup process will start again.

StopService process

StopService by calling context.stopservice:

[ContextImpl#stopService]

public boolean stopService(Intent service) {
    warnIfCallingFromSystemProcess();
    return stopServiceCommon(service, mUser);
}

private boolean stopServiceCommon(Intent service, UserHandle user) {
    try {
        / /...
        // request AMS to execute stopService scheduling
        int res = ActivityManager.getService().stopService(
            mMainThread.getApplicationThread(), service,
            service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
        / /...
    } catch (RemoteException e) {
        throwe.rethrowFromSystemServer(); }}Copy the code

The stopServiceLocked method of ActivityManagerService is the same as the startService method:

[ActiveServices#stopServiceLocked]

int stopServiceLocked(IApplicationThread caller, Intent service,
        String resolvedType, int userId) {
    / /...
    // If this service is active, make sure it is stopped.
    ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, null,
            Binder.getCallingPid(), Binder.getCallingUid(), userId, false.false.false.false);
    if(r ! =null) {
        if(r.record ! =null) {
            final long origId = Binder.clearCallingIdentity();
            try {
                stopServiceLocked(r.record);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return 1;
        }
        return -1;
    }

    return 0;
}
Copy the code

First look up the target ServiceRecord, then call the stopServiceLocked method.

The stopServiceLocked method will eventually call the bringDownServiceLocked method: [ActiveServices#bringDownServiceLocked]

private final void bringDownServiceLocked(ServiceRecord r) {
    / /...
    
    if(r.app ! =null) {
        if(r.app.thread ! =null) {
            try {
                / /...
                r.app.thread.scheduleStopService(r);
            } catch (Exception e) {
                / /...}}else {
            / /...}}else {
        / /...
    }
    
    / /...
}
Copy the code

Schedule the application process to stop.

ScheduleStopService sends the H.top_service message, which ultimately executes the handleStopService method:

[ActivityThread#handleStopService]

private void handleStopService(IBinder token) {
    // Get the cached Service instance
    Service s = mServices.remove(token);
    if(s ! =null) {
        try {
            if (localLOGV) Slog.v(TAG, "Destroying service " + s);
            // Execute the onDestroy callback
            s.onDestroy();
            s.detachAndCleanUp();
            Context context = s.getBaseContext();
            if (context instanceof ContextImpl) {
                final String who = s.getClassName();
                ((ContextImpl) context).scheduleFinalCleanup(who, "Service");
            }

            QueuedWork.waitToFinish();

            try {
                / / notify the AMS
                ActivityManager.getService().serviceDoneExecuting(
                        token, SERVICE_DONE_EXECUTING_STOP, 0.0);
            } catch (RemoteException e) {
                throwe.rethrowFromSystemServer(); }}catch (Exception e) {
            / /...}}else {
        Slog.i(TAG, "handleStopService: token=" + token + " not found.");
    }
    //Slog.i(TAG, "Running services: " + mServices);
}
Copy the code

Application side stopService removes the Service instance from the mServices collection, executes its onDestroy lifecycle callback method, and notifies AMS.

conclusion

Services are started and stopped by sending requests to ActivityManagerService, which is scheduled for distribution by AMS. Service running information is recorded in ServiceRecord on AMS, and Service instances created by reflection are stored in mServices collection on application. After AMS sends start or stop instructions to the application side, the application side performs the corresponding life cycle callback on the Service instance.

These two processes are summarized in sequence diagrams below. Startup process:

Stop process: