preface

After a lengthy introduction to the startup process of an Android application (the root Activity startup process), we continue this article by examining the Service startup process. Before reading this article, you are advised to read the following two articles about Android’s four major components (I) and application startup process (II).

1.ContextImpl Call to ActivityManageService

To start the Service, we call the startService method, which is implemented in ContextWrapper with the code shown below. frameworks/base/core/java/android/content/ContextWrapper.java

public class ContextWrapper extends Context { Context mBase; .@Override
    public ComponentName startService(Intent service) {
        returnmBase.startService(service); }... }Copy the code

The startService method of mBase is called in the startService method. What does the Context mBase object refer to? When an ActivityThread starts an Activity, it calls the following code to create an Activity context. frameworks/base/core/java/android/app/ActivityThread.java

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...if(activity ! =null) {
                Context appContext = createBaseContextForActivity(r, activity);/ / 1. } 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); . }return activity;
}Copy the code

In the attach method of the Activity, we attach the Activity to appContext. What is the specific type of appContext? We then look at createBaseContextForActivity method, the code is shown below. frameworks/base/core/java/android/app/ActivityThread.java

private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {... ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token, displayId, r.overrideConfig); appContext.setOuterContext(activity); Context baseContext = appContext; .return baseContext;
}Copy the code

It follows that the specific type of the context object appContext is ContextImpl. The attach method of the Activity assigns ContextImpl to the ContextWrapper member variable mBase, so the mBase refers to the ContextImpl. So, let’s next look at the ContextImpl startService method, as shown below. frameworks/base/core/java/android/app/ContextImpl.java

Override
public ComponentName startService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, mUser);
}
 private ComponentName startServiceCommon(Intent service, UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            / * * * 1 * /ComponentName cn = ActivityManagerNative.getDefault().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( getContentResolver()), getOpPackageName(), user.getIdentifier()); .return cn;
        } catch (RemoteException e) {
            throwe.rethrowFromSystemServer(); }}Copy the code

The startService method returns the startServiceCommon method, The startService method of ActivityManageService (AMS) proxy object ActivityManagerProxy (AMP) is called in the startServiceCommon method at comment 1, AMS’s startService method is eventually called. As for why the code at comment 1 calls the AMS startService method, it has been explained in this article, which will not be repeated here. The ContextImpl call to ActivityManageService is shown in the sequence diagram below.

2. ActivityThread start Service

Let’s next look at the AMS startService method. frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

Override
public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, String callingPackage, int userId)
        throws TransactionTooLargeException {...synchronized(this) {... ComponentName res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, callingPackage, userId);/ / 1
        Binder.restoreCallingIdentity(origId);
        returnres; }}Copy the code

Note 1 calls the startServiceLocked method of mServices, which is of type ActiveServices, as shown below. frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, String callingPackage, final int userId)
            throws TransactionTooLargeException {...return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    }

   ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {... String error = bringUpServiceLocked(r, service.getFlags(), callerFg,false.false); .return r.name;
    }Copy the code

The end of the startServiceLocked method returns the startServiceInnerLocked method, which in turn calls the bringUpServiceLocked method: frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

  private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {...final String procName = r.processName;/ / 1
  ProcessRecord app;
  if(! isolated) { app = mAm.getProcessRecordLocked(procName, r.appInfo.uid,false);/ / 2
            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                        + " app=" + app);
            if(app ! =null&& app.thread ! =null) {/ / 3
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    realStartServiceLocked(r, app, execInFg);/ / 4
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting service "+ r.shortName, e); }}}else {
            app = r.isolatedProc;
        }

 if (app == null && !permissionsReviewRequired) {/ / 5
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated, false)) = =null) {/ / 6. }if(isolated) { r.isolatedProc = app; }}... }Copy the code

The processName value of ServiceRecord is assigned to procName in comment 1, where ServiceRecord describes the Android: Process property of the Service. Note 2 passes the procName and Service’s UID into AMS’s getProcessRecordLocked method to see if there is an object app of type ProcessRecord corresponding to the Service. ProcessRecord is used to record information about the running application process. If the app corresponding to Service is null in comment 5, the application process used to run Service does not exist. Call the startProcessLocked method of AMS in comment 5 to create the corresponding application process. For information about creating an application process, see the Android Application Process Startup (previous) and Android Application Process Startup (later) articles. If the application process used to run the Service exists, call the realStartServiceLocked method in comment 4:

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {...try{... app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState); r.postNotification(); created =true;
    } catch(DeadObjectException e) { ... }... }Copy the code

The scheduleCreateService method of app. Thread is called in realStartServiceLocked. App. Thread is of type IApplicationThread, which is implemented as the inner class ApplicationThread of ActivityThread. ApplicationThread inherits ApplicationThreadNative, and ApplicationThreadNative inherits Binder and implements IApplicationThread interface. The scheduleCreateService methods of ApplicationThread are shown below. frameworks/base/core/java/android/app/ActivityThread.java

   public final void scheduleCreateService(IBinder token,
            ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
        updateProcessState(processState, false);
        CreateServiceData s = new CreateServiceData();
        s.token = token;
        s.info = info;
        s.compatInfo = compatInfo;
        sendMessage(H.CREATE_SERVICE, s);
    }Copy the code

The start information is encapsulated into a CreateServiceData object and passed to the sendMessage method, which sends a CREATE_SERVICE message to H, an inner class of ActivityThread that inherits from Handler. This process is similar to the application startup process (the root Activity startup process). Let’s next look at H’s handleMessage method. frameworks/base/core/java/android/app/ActivityThread.java

  public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
            ...
               case CREATE_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break; . }... }... }Copy the code

The handleMessage method calls the handleCreateService method, depending on the message type:

frameworks/base/core/java/android/app/ActivityThread.java

 private void handleCreateService(CreateServiceData data) {
        unscheduleGcIdler();
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);/ / 1
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();/ / 2
            service = (Service) cl.loadClass(data.info.name).newInstance();/ / 3
        } catch(Exception e) { ... }}try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);/ / 4
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());/ / 5
            service.onCreate();/ / 6
            mServices.put(data.token, service);/ / 7. }catch(Exception e) { ... }}Copy the code

Comment 1 gets the LoadedApk for the application to start the Service. LoadedApk is a description class for an APK file. Note 2 gets the classloader by calling LoadedApk’s getClassLoader method. The Service is then loaded into memory in comment 3 based on the Service information stored in the CreateServiceData object. Note 4 creates the ContextImpl object for the Service. Note 5 initializes the Service with the attach method of the Service. The Service is started by calling the onCreate method of the Service in comment 6. At comment 7, add the started Service to the member variable mServices of ActivityThread, where mServices is of type ArrayMap. Finally, the sequence diagram of this section is given.