Analysis of the main process of startService source code

Article directory


This article is based on Android 8.0 source code.

1. Calling startService in Activity

When we start the service, we call the startService method directly in the Activity. In fact, we call the contextimpl ා startService method.

We know that activity inherits from ContextThemeWrapper, ContextThemeWrapper inherits from ContextWrapper, ContextWrapper is just the static agent of context, and the implementation class of context is contextimpl. After creating Activity, the system calls the Activity#attach method to set the created ContextImpl instance object to Activity, so the method of calling Context objects in Activity basically goes through ContextImpl implementation.

2,ContextImpl#startService

ContextImpl#startService 
--> ContextImpl#startServiceCommon
--> ActivityManagerService#startService (called through the Binder object of AMS)

3,ActivityManagerService#startService

Let's take a look at the activitymanagerservice ා startservice method,

ActivityManagerService#startService
--> ActiveService#startServiceLocked (a ServiceRecord.StartItem object is generated based on the current service object and added to pendingStarts for later onStartCommand method calls)
--> ActiveService#startServiceInnerLocked
--> ActiveService#bringUpServiceLocked

The activeservice ා bringupservicelocked method here is the core. Let's look at the following key code:

Here we divide the code into four parts, and the core logic is explained in the comments.

private String bringUpServiceLocked(ServiceRecord r, ...){
    
    // 1
    // If the conditions are met, the service has been started, so the process corresponding to the service has been started, and the ActivityThread corresponding to the process has been prepared. Next, directly inform the application process to call the onStartCommand method of the service.
    if (r.app != null && r.app.thread != null) {
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }

    // 2
    // It means that the service has not been started. Let's check whether the process where the service is started
    // If the process has started and the ActivityThread is ready, start the service.
    ProcessRecord app = mAm.getProcessRecordLocked(procName,...);
    if (app != null && app.thread != null) {
       realStartServiceLocked(r, app, execInFg);
    }

    // 3
    // If the process of the service is not started, start the process
    if (app == null) {
        app=mAm.startProcessLocked(procName,...)
    }

    // 4 add the service to the mPendingServices list and wait for the application to start before starting the service
    if (!mPendingServices.contains(r)) {
        mPendingServices.add(r);
    }
    
    return null;
}

The above process can be summarized as follows:

Next, we will talk about the details, how to start the process, and how to start the service after the process starts.

4. Start process, execute services in mPendingServices

Zygote start process

In the activeservice? Bringupservicelocked method, the code to start the process is as follows:

app=mAm.startProcessLocked(procName,...)

Then look down:

ActivityManagerService#startProcessLocked
--> ActivityManagerService#startProcessLocked (overload method)
--> ActivityManagerService#startProcessLocked (overload method)
--> Process.start
--> ZygoteProcess.start
--> ZygoteProcess.startViaZygote

Let's look at the ZygoteProcess.startViaZygote method. The code is as follows:

private Process.ProcessStartResult startViaZygote(final String processClass,...){
    ArrayList<String> argsForZygote = new ArrayList<String>();
    ...
    return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}

The main function of zygoteSendArgsAndGetResult method is to write the starting parameter argsForZygote of the incoming application process to ZygoteState, which is the static inner class of ZygoteProcess, used to represent the state of communication with the Zygote process.

Then look at the openzygotesocketifneed method, which internally calls the ZygoteState.connect(mSocket) method to establish a Socket connection with the Zygote process.

Socket of Zygote process -- receive the message of creating process and fork to create sub process

Next, let's look at the Socket started in the Zygote process. First, let's look at the zygoteinitාmain function:
Here we only look at Socket related

public static void main(String argv[]) {
    ZygoteServer zygoteServer = new ZygoteServer();
    String socketName = "zygote";
    
    // Start Socket with the name zygote
    zygoteServer.registerServerSocket(socketName);

    // Open the while loop, receive Socket messages and process them
    zygoteServer.runSelectLoop(abiList);

    zygoteServer.closeServerSocket();
}

The logic of Socket receiving message is in ZygoteServer.runSelectLoop method. After receiving the message, zygoteconnection ා RunOnce method will be called. Let's look at the key code:

boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
    Arguments parsedArgs = null;
    
    pid = Zygote.forkAndSpecialize(parsedArgs.uid,...);
   
    if (pid == 0) {
        // in child
        handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
        return true;
    } else {
       ...
    }
}

First call the Zygote#forkAndSpecialize method to start the process, the fork method will return two times, pid is 0, that is the child process we care about, then we call the Zygote#handleChildProc method to process, the Zygote#handleChildProc method will call the ZygoteInit.zygoteInit method, its key code is as follows:

public static final void zygoteInit(int targetSdkVersion,...){
    RuntimeInit.commonInit();
    // Create Binder thread pool for application process
    ZygoteInit.nativeZygoteInit();
    // Call the activitythread × main method of the process
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

The runtimeinit × applicationinit method will call the runtimeinit × invokestaticmain method. The specific code is as follows:

/**
 * Invokes a static "main(argv[]) method on class "className".
 * Converts various failing exceptions into RuntimeExceptions, with
 * the assumption that they will then cause the VM instance to exit.
 *
 * @param className Fully-qualified class name
 * @param argv Argument vector for main()
 * @param classLoader the classLoader to load {@className} with
 */
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
        throws Zygote.MethodAndArgsCaller {
    Class<?> cl;

    try {
        cl = Class.forName(className, true, classLoader);
    } catch (ClassNotFoundException ex) {
        throw new RuntimeException(
                "Missing class when invoking static main " + className,
                ex);
    }

    Method m;
    try {
        m = cl.getMethod("main", new Class[] { String[].class });
    } catch (NoSuchMethodException ex) {
        throw new RuntimeException(
                "Missing static main on " + className, ex);
    } catch (SecurityException ex) {
        throw new RuntimeException(
                "Problem getting static main on " + className, ex);
    }

    int modifiers = m.getModifiers();
    if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
        throw new RuntimeException(
                "Main method is not public and static on " + className);
    }

    /*
     * This throw gets caught in ZygoteInit.main(), which responds
     * by invoking the exception's run() method. This arrangement
     * clears up all the stack frames that were required in setting
     * up the process.
     */
    throw new Zygote.MethodAndArgsCaller(m, argv);
}

The function of the runtimeinit ා invokestaticmain method is very clear, which is to call the main(argv []) method of its input parameter className, where the value of className is android.app.ActivityThread.

Activitythread ා main -- notify AMS process to finish creation, initialize Application,

After Zygote starts our application process, it will call the process's entry function android.app.activitythread × main. Next let's see how activitythread ා main starts the service.

ActivityThread#main
--> ActivityThread#attach
--> ActivityManagerService#attachApplication(mAppThread) 

The activitymanagerservice ා attachapplication method will submit the Binder object of the ActivityThread of the application process to AMS, so that AMS and the application process can start the two-way call. Then look down:

ActivityManagerService#attachApplication(mAppThread)
--> ActivityThread#bindApplication (method to call ActivityThread through Binder)
--> ActivityThread#handleBindApplication (switch to main thread through Handler)
Initialize Application instance

In the activitythread ා handlebindinapplication method, the Application object is created by calling loadedapp ා makeapplication, and its life cycle method is called. The core code of loadedapp ා makeapplication is as follows:

public Application makeApplication(...) {
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    app = mActivityThread.mInstrumentation.newApplication(...);
    appContext.setOuterContext(app);

    // Call the application oncreate method
    instrumentation.callApplicationOnCreate(app);

    return app;
}

The specific process of creating Application object is as follows:

LoadedApk#makeApplication
--> Instrumentation#newApplication
--> Instrumentation#Newapplication initializes the Application instance
--> Application#attach
--> Application#attachBaseContext 

Now that our Application process and Application are initialized, let's look at how to place the service in mPendingServices after the process is started.

Activitymanagerservice? Attachapplicationlocked -- start the service after the process starts

As mentioned before, after the process is started, ActivityThread will report to AMS, and it will call activitymanagerservice ා attachapplicationlocked method. It is the old rule. We only look at the relevant core content:

private final boolean attachApplicationLocked(IApplicationThread thread,...) {
    // Call the activitythread ා bindapplication method on the Application side to complete Application initialization
    thread.bindApplication(processName, ...);

    // Find any services that should be running in this process...
    // Call the activeservices? Attachapplicationlocked method to execute the service in mPendingServices
    didSomething |= mServices.attachApplicationLocked(app, processName);
}

In the activeservices? Attachapplicationlocked method, the mPendingServices will be traversed, and the activeservices? Realstartservicelocked method will be executed for each service. The specific code is as follows:

boolean attachApplicationLocked(ProcessRecord proc, String processName){
    if (mPendingServices.size() > 0) {
        ServiceRecord sr = null;
        for (int i=0; i<mPendingServices.size(); i++) {
            sr = mPendingServices.get(i);
            mPendingServices.remove(i);
            i--;
            realStartServiceLocked(sr, proc, sr.createdFromFg);
        }
    }
}

In the activeservices ා realstartservicelocked method, the activitythread ා schedulecreateservice method will be called through Binder to tell the application to start the Service; then the activeservices ා sendserviceargslocked method will be called through Binder to call the activitythread ා scheduleserviceargs method, and finally the Service ා onstartcommand method. See the details below.

Start service - activitythread - schedulecreateservice

The call chain is as follows:

ActivityThread#scheduleCreateService
--> Send out`H.CREATE_SERVICE`Message to main thread
--> ActivityThread#handleCreateService

In the activitythread ා handlecreateservice method, the Service class will be initialized and its onCreate method will be called. The core code is as follows:

private void handleCreateService(CreateServiceData data) {
    // Initialize Service object
    java.lang.ClassLoader cl = packageInfo.getClassLoader();
    Service service = (Service) cl.loadClass(data.info.name).newInstance();

    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
    context.setOuterContext(service);
    // Get the corresponding Application object
    Application app = packageInfo.makeApplication(false, ...);
    // Call the service × oncreate() method
    service.onCreate();
}
onStartCommand of calling service -- activitythread ා (scheduleserviceargs)

The call chain is as follows:

ActivityThread#scheduleServiceArgs
--> Send out`H.SERVICE_ARGS`Message to main thread
--> ActivityThread#handleServiceArgs
--> Service#onStartCommand

So far, the overall process of starting application + starting service for the first time is completed.

Process summary

At this point, the key steps of a complete startService process are analyzed.

114 original articles published, 53 praised, 130000 visitors+
Private letter follow

Tags: socket Android Java

Posted on Mon, 16 Mar 2020 06:54:58 -0700 by Psyche