APP performance optimization startup process analysis

1. Analysis of the startup process of the Android system

  1. BootLoader starts the kernel and init process;
  2. The init process splits out a daemon process, such as Android Debug Damon, USB Damon, these daemon processes will handle some hardware-related interfaces;
  3. The init process starts a Zygote process. The Zygote process initializes the first VM and preloads the Framework and some common resources.
    The zygote process will open a Socket interface to monitor requests. Once a request is received, Zygote will incubate a new VM based on its own preloaded VM and create a new process.
  4. After starting Zygote, the init process will start the Runtime process. Zygote will hatch a super management process - System Server. System Server will start all system core services, such as Activity Manager Service and hardware-related services.
  5. At this time, it is ready to start its first App process-Home process.

The Android system has been started, and some core services have also been started, and then the Launcher application is started.

2. Analysis of App startup process

When is the App process created?

When an application calls a page in another application, if the target process does not exist, a new process will be created and started.

Application startup process

First look at the flow chart

  1. Click the icon on the desktop or click to jump to another application in the foreground application
  2. Then call the StartActivity(Intent intent) method;
    this method will eventually call ActivityManagerService through Binder IPC, here referred to as AMS.
  3. AMS does the following:
    1. Finally, the pointing information of this Intent object will be collected through the resolveIntent() method of PackageManager (there will be many classes and method calls in the middle).
    2. Use the grantUriPermissionLocked() method to verify whether the user has sufficient permissions to call the target Activity;
    3. Query whether ProcessRecord exists.
      If not, AMS will create a new process to instantiate the target Activity.

Next, let's talk about the creation process of the App process.

App process creation

  1. Call the startProcessLocked() method to create a new process and pass parameters to the Zygote process through the Socket channel mentioned above. The Zygote process incubates itself, and calls the ZygoteInit.main() method to instantiate the ActivityThread object, and finally returns the pid of the new process.
  2. ActivityThread calls the Looper.prepare() and Looper.loop() methods in turn to open the message loop.

At this time, the process has been created, but how to link it with the Application itself?

Application binding

  1. Call the bindApplication() method in ActivityThread to send a BIND_APPLICATION message to the message queue.
  2. Process the previous binding message through the handleBindApplication() method;
  3. Call the makeApplication() method to load the Application class into memory.

The following is a partial source code snippet

ActivityThread.java

sendMessage(H.BIND_APPLICATION, data);

@Override
public final void bindApplication(String processName, ApplicationInfo appInfo,
                                  ProviderInfoList providerList, ComponentName instrumentationName,
                                  ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                                  IInstrumentationWatcher instrumentationWatcher,
                                  IUiAutomationConnection instrumentationUiConnection, int debugMode,
                                  boolean enableBinderTracking, boolean trackAllocation,
                                  boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                                  CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                                  String buildSerial, AutofillOptions autofillOptions,
                                  ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges,
                                  SharedMemory serializedSystemFontMap) {
    if (services != null) {
        if (false) {
            // Test code to make sure the app could see the passed-in services.
            for (Object oname : services.keySet()) {
                if (services.get(oname) == null) {
                    continue; // AM just passed in a null service.
                }
                String name = (String) oname;

                // See b/79378449 about the following exemption.
                switch (name) {
                    case "package":
                    case Context.WINDOW_SERVICE:
                        continue;
                }

                if (ServiceManager.getService(name) == null) {
                    Log.wtf(TAG, "Service " + name + " should be accessible by this app");
                }
            }
        }

        // Setup the service cache in the ServiceManager
        ServiceManager.initServiceCache(services);
    }

    setCoreSettings(coreSettings);

    AppBindData data = new AppBindData();
    data.processName = processName;
    data.appInfo = appInfo;
    data.providers = providerList.getList();
    data.instrumentationName = instrumentationName;
    data.instrumentationArgs = instrumentationArgs;
    data.instrumentationWatcher = instrumentationWatcher;
    data.instrumentationUiAutomationConnection = instrumentationUiConnection;
    data.debugMode = debugMode;
    data.enableBinderTracking = enableBinderTracking;
    data.trackAllocation = trackAllocation;
    data.restrictedBackupMode = isRestrictedBackupMode;
    data.persistent = persistent;
    data.config = config;
    data.compatInfo = compatInfo;
    data.initProfilerInfo = profilerInfo;
    data.buildSerial = buildSerial;
    data.autofillOptions = autofillOptions;
    data.contentCaptureOptions = contentCaptureOptions;
    data.disabledCompatChanges = disabledCompatChanges;
    data.mSerializedSystemFontMap = serializedSystemFontMap;
    sendMessage(H.BIND_APPLICATION, data);
}

handleMessage(msg)

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case BIND_APPLICATION:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
            AppBindData data = (AppBindData)msg.obj;
            handleBindApplication(data);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        ...
    }
    ...
}

handleBindApplication(data)

 @UnsupportedAppUsage
    private void handleBindApplication(AppBindData data) {
        ...
        try {
            // If the app is being launched for full backup or restore, bring it up in
            // a restricted environment with the base application class.
            app = data.info.makeApplication(data.restrictedBackupMode, null);

            // Propagate autofill compat state
            app.setAutofillOptions(data.autofillOptions);

            // Propagate Content Capture options
            app.setContentCaptureOptions(data.contentCaptureOptions);
            sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName);

            mInitialApplication = app;
            final boolean updateHttpProxy;
            synchronized (this) {
                updateHttpProxy = mUpdateHttpProxyOnBind;
                // This synchronized block ensures that any subsequent call to updateHttpProxy()
                // will see a non-null mInitialApplication.
            }
            if (updateHttpProxy) {
                ActivityThread.updateHttpProxy(app);
            }

            // don't bring up providers in restricted mode; they may depend on the
            // app's custom Application class
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                }
            }
            ...
        }
        
        ...
    }

Note the data.info.makeApplication(data.restrictedBackupMode, null) and installContentProviders(app, data.providers)

data.info type is LoadedApk

LoadedApk.java

@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
                                   Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }

    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

    Application app = null;

    String appClass = mApplicationInfo.className;
    if (forceDefaultAppClass || (appClass == null)) {
        appClass = "android.app.Application";
    }

    try {
        final java.lang.ClassLoader cl = getClassLoader();
        if (!mPackageName.equals("android")) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                             "initializeJavaContextClassLoader");
            initializeJavaContextClassLoader();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }

        // Rewrite the R 'constants' for all library apks.
        SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
            false, false);
        for (int i = 0, n = packageIdentifiers.size(); i < n; i++) {
            final int id = packageIdentifiers.keyAt(i);
            if (id == 0x01 || id == 0x7f) {
                continue;
            }

            rewriteRValues(cl, packageIdentifiers.valueAt(i), id);
        }

        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        // The network security config needs to be aware of multiple
        // applications in the same process to handle discrepancies
        NetworkSecurityConfigProvider.handleNewApplication(appContext);
        app = mActivityThread.mInstrumentation.newApplication(
            cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        if (!mActivityThread.mInstrumentation.onException(app, e)) {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            throw new RuntimeException(
                "Unable to instantiate application " + appClass
                + " package " + mPackageName + ": " + e.toString(), e);
        }
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;

    if (instrumentation != null) {
        try {
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            if (!instrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                    "Unable to create application " + app.getClass().getName()
                    + ": " + e.toString(), e);
            }
        }
    }

    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

    return app;
}

Among them, mApplicationInfo.className is the Application customized by App. If it is empty, the default appClass = "android.app.Application" will be used. initializeJavaContextClassLoader() is the logic call for initializing the class loader, and you can continue to track its logic. It can be found that after the sharedUserId is set to the same, the logic of a virtual machine can be shared

app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext) At this time, you can see that it is the instantiation of Application in Instrumentation

Look at some excerpts of code

ActivityThread.java

mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);

...
// Restore instance state
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState);

...
// Call postOnCreate()
mInstrumentation.callActivityOnPostCreate(activity, r.state, r.persistentState);

...
mInstrumentation.callActivityOnNewIntent(r.activity, intent);

...
 mInstrumentation.callActivityOnPause(r.activity);

...
mInstrumentation.callActivityOnDestroy(r.activity);

...
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);

...
mInstrumentation.callApplicationOnCreate(app);

Among them: callActivityOnCreate, callApplicationOnCreate, newActivity, callActivityOnNewIntent, etc. In all life cycle calls of application and activity through Instrumentation in the ActivityThread class, the corresponding methods of Instrumentation will be called first to realize the corresponding functions. There are more correct related functions in the Instrumentation class. method.

Continue into Instrumentation.java

 static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

Here is the time to create the Application class

Application.java

final void attach(Context context) {
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

Finally found the attachBaseContext method we are most familiar with.
Let's find the onCreate timing of ContentProvider again, first go back to the above-mentioned publishContentProviders method of ActivityThread.java, you can see that there is an
installContentProviders method that was executed earlier, check this method

ActivityThread.java

 @UnsupportedAppUsage
    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(": ");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
private IActivityManager.ContentProviderHolder installProvider(Context context,IActivityManager.ContentProviderHolder holder, ProviderInfo info,boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ...
    final java.lang.ClassLoader cl = c.getClassLoader();
                localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
                    return null;
                }
                if (DEBUG_PROVIDER) Slog.v(
                    TAG, "Instantiating local provider " + info.name);
                // XXX Need to create the correct context for this provider.
                localProvider.attachInfo(c, info);
    ...

}

Here load the ContentProvider class instance from ClassLoader and call the attachInfo method

Let's look at ContentProvider.java

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
    ...
    ContentProvider.this.onCreate();
    ...
}

A brief summary of the above startup process:

  1. ActivityThread.attath()
  2. AMS.attachApplication()
  3. ActivityThread.handleBindApplication()
  4. Application.attachBaseContext()
  5. ActivityThread.installContentProviders
  6. ContentProvider.onCreate()
  7. AMS.publishContentProviders()
  8. Application.onCreate()

So for App startup optimization, we can intervene from three aspects: Application.attachBaseContext(), ContentProvider.onCreate(), and Application.onCreate().

 

Guess you like

Origin blog.csdn.net/u011106915/article/details/125389912