Android startup - Application startup process

Keywords: Java Android Design Pattern

Based on Android 11-api30

Overview

  1. Obtain two binders, applicationThread and AMS
    2. During attach, the obtained applicationThread object is also passed to the AMS process, requesting a remote call to inform the AMS Application process that it wants to create an Application. At this time, AMS is the server
  2. AMS receives a message and requests to call the remote interface of applicationThread. At this time, AMS is the client
  3. applicationThread receives the request from AMS and initiates the processing task of creating an Application through the Handler. There will be no remote interface call later
  4. Create an instance of Application through reflection, and start the onCreate method of Application through Instrumentation

Detailed process analysis

Start with the main method of ActivityThread.java;

public static void main(String[] args) {
    ...
    ActivityThread thread = new ActivityThread();
    thread.attach(system=false, startSeq);//1
    ...
}

Enter the attach method;

if(!system){
    final IActivityManager mgr = ActivityManager.getService();
    try {
        mgr.attachApplication(mAppThread, startSeq);//1
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

For non system application processes, it can be concluded from getSeervice and the captured RemoteException that Binder is used for remote interface calls here.
Turn around and see what mAppThread is?

final ApplicationThread mAppThread = new ApplicationThread();

private class ApplicationThread extends IApplicationThread.Stub {
    //Batch schedule * interfaces, such as scheduleReceiver, scheduleCreateService, etc
    public final void schedule*
    
    //TODO key methods
    public final void bindApplication(some args){}//1
    
    //A bunch of dump methods, such as dumpMemory, dumpActivity, etc
    
}

As you can see, ApplicationThread is a Binder client that implements remote interfaces, and many remote interfaces are encapsulated and implemented internally. However, when the client connects to the server is unknown. The bindService keyword is not found. Anyway, the corresponding Service should be connected at this time. It should be started when the application process is started in the RuntimeInit.java class.

Let's look back at the internal implementation of the service instance IActivityManager.attachApplication() in the previous step.

  1. First obtain the AMS instance. The AMS instance code obtained here is consistent with that in the Activity startup process
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;
            }
        };

... after obtaining the Binder of AMS, continue to view the attachApplication method in ActivityManagerService.java

public final void attachApplication(IApplicationThread thread, long startSeq) {
    
   	synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid, callingUid, startSeq); //1
        Binder.restoreCallingIdentity(origId);
    }
}

The AMS instance is obtained by a single instance. The AMS service has been registered with the ServiceManager since the system is started. Here, you can directly obtain the Binder instance. The ServiceManager manages the registered Server in the form of Binder pool.

In the attachApplication method of AMS, enter the attachApplicationLocked method, pick up the understandable code, and view the code with the thread parameter.

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {

    try {
        AppDeathRecipient adr = new AppDeathRecipient(
                app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);//1
        app.deathRecipient = adr;
    } catch (RemoteException e) {
        app.resetPackageList(mProcessStats);
        mProcessList.startProcessLocked(app,
                new HostingRecord("link fail", processName),
                ZYGOTE_POLICY_FLAG_EMPTY);
        return false;
    }

    final ActiveInstrumentation instr2 = app.getActiveInstrumentation();

    if (instr2 != null) {//2
        thread.bindApplication(processName, appInfo, providerList,
                instr2.mClass,
                profilerInfo, instr2.mArguments,
                instr2.mWatcher,
                instr2.mUiAutomationConnection, testMode,
                mBinderTransactionTrackingEnabled, enableTrackAllocation,
                isRestrictedBackupMode || !normalMode, app.isPersistent(),
                new Configuration(app.getWindowProcessController().getConfiguration()),
                app.compat, getCommonServicesLocked(app.isolated),
                mCoreSettingsObserver.getCoreSettingsLocked(),
                buildSerial, autofillOptions, contentCaptureOptions,
                app.mDisabledCompatChanges);
    } else {
        thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
                null, null, null, testMode,
                mBinderTransactionTrackingEnabled, enableTrackAllocation,
                isRestrictedBackupMode || !normalMode, app.isPersistent(),
                new Configuration(app.getWindowProcessController().getConfiguration()),
                app.compat, getCommonServicesLocked(app.isolated),
                mCoreSettingsObserver.getCoreSettingsLocked(),
                buildSerial, autofillOptions, contentCaptureOptions,
                app.mDisabledCompatChanges);
    }

}

First give the Binder, ApplicationThread, a death agent. According to the death agent, you should be able to find out how the corresponding Service is restarted. If you are interested, you can continue to go deeper. Let's continue.
Call the thread.bindApplication interface here. We saw it when we looked at the ApplicationThread earlier, and cut in directly.

private class ApplicationThread extends IApplicationThread.Stub {
    //Batch schedule * interfaces, such as scheduleReceiver, scheduleCreateService, etc
    public final void schedule*
    
    //TODO key methods
    public final void bindApplication(some args){
        AppBindData data = new AppBindData();
        ...A bunch of parameters
        sendMessage(H.BIND_APPLICATION, data);//1
    }
    
    //A bunch of dump methods, such as dumpMemory, dumpActivity, etc
    
}

After reaching the point that we Android development engineers are familiar with, we encapsulated a bunch of parameters and sent a bind through the Handler object H_ Application message, let's see where this message goes and follow up bind directly_ Application is the capture location of this message.

//Message distribution
class H extends Handler{
    public void handleMessage(Message msg){
        swich(msg.what){
            case BIND_APPLICATION: 
                AppBindData data = (AppBindData)msg.obj;
                handleBindApplication(data);//1
                break;
            ...ellipsis
        }
    }
}

Enter the message distribution processing method. This method is relatively long. Pay attention to the code that can be understood without understanding, and track the processing of data.

private void handleBindApplication(AppBindData data) {
    //Various initializations, such as process name, application name, AsyncTask thread pool configuration, time zone, and network discovery
    
    //Initialization of Context
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
    
    try {
        final ClassLoader cl = instrContext.getClassLoader();
        mInstrumentation = (Instrumentation)//1
            cl.loadClass(data.instrumentationName.getClassName()).newInstance();
    } catch (Exception e) {
        throw new RuntimeException(
            "Unable to instantiate instrumentation "
            + data.instrumentationName + ": " + e.toString(), e);
    }
    
    final ComponentName component = new ComponentName(ii.packageName, ii.name);
    mInstrumentation.init(this, instrContext, appContext, component,//1
            data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
            
    ...
    Application app;
    app = data.info.makeApplication(data.restrictedBackupMode, null);//2

    mInstrumentation.onCreate(data.instrumentationArgs);
    mInstrumentation.callApplicationOnCreate(app);//3
}

Instantiate the mminstrumentation object through reflection, which is the housekeeper of Android system components. At present, it can control the life cycle of Application and Activity.

Create an Application object, go in and have a look at the created code

//LoadApk.java #makeApplication
public Application makeApplication(boolean forceDefaultAppClass,
    Instrumentation instrumentation){
    ...
    app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);//1
    appContext.setOuterContext(app);
    ...
}

//Instrumentation.java #newApplication
public Application newApplication(ClassLoader cl, String className, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = getFactory(context.getPackageName())
            .instantiateApplication(cl, className);//2
    app.attach(context);//First, call back the attachBaseContext method
    return app;
}

//AppComponentFactory #instantiateApplication
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
        @NonNull String className)
        throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    return (Application) cl.loadClass(className).newInstance();//3
}

You can see that the Application is finally initialized through reflection.

Finally, the onCreate method of the Application class is called through the MI instrumentation object.

mInstrumentation.callApplicationOnCreate(app);//1

//Instrumentation.java #callApplicationOnCreate
public void callApplicationOnCreate(Application app) {
    app.onCreate();
}

Posted by Ambush Commander on Tue, 19 Oct 2021 13:12:48 -0700