Android Design Patterns (4) Continuation: How to Call Activity's onCreate

Keywords: Android Java

Here we analyze the startup process of the onCreate() method of the first Activity of app at its first startup.

chart

First, stick up a rough flow chart. It's ugly. It can't be drawn.


ActivityThread.main()

The startup entry of APP should be known as the main method of ActivityThread, so the entire startup of APP starts here until we see the home page. Not much code:

package android.app;
public final class ActivityThread {
    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
        CloseGuard.setEnabled(false);
        Environment.initForCurrentUser();
        EventLogger.setReporter(new EventLoggingReporter());
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
        Process.setArgV0("<pre-initialized>");
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}

In fact, through this code we can find an interesting phenomenon that APP can run all the time without exiting because the last Looper.loop() of the main method is a dead loop.

When an APP starts, the system will start a new thread from the Zygone thread for the application fork, and then execute the main method of ActivityThread in the thread.
The main process of main method is:

  • Prepare a Loper for the main thread
  • Create a new ActivityThread and execute its attach method
  • Open Looper's loop method to start reading and processing messages from message queues in an infinite loop.

    ActivityThread.attach(false)

    Let's take a look at ActivityThread.attach(), and the parameter passed in in main() is FALSE, indicating that this APP is not the system APP. So we mainly look at not the processing of system APP.
    private void attach(boolean system) {
          sCurrentActivityThread = this;
          mSystemThread = system;
          if (!system) {
    //Not System APP
              ViewRootImpl.addFirstDrawHandler(new Runnable() {
                  @Override
                  public void run() {
    //Determine that the virtual machine is working properly
                      ensureJitEnabled();
                  }
              });
            //...
    //Get an Activity Manager Service AMS
              final IActivityManager mgr = ActivityManagerNative.getDefault();
              try {
    //MAppThread = new Application Thread (), bound to AMS
                  mgr.attachApplication(mAppThread);
              } catch (RemoteException ex) {
                  throw ex.rethrowFromSystemServer();
              }
             //...
          } else {
             //...
          }
    Activity Manager Native. getDefault () is to get an AMS, as you can see from the following code, using the singleton pattern obtained from System Manager.
    package android.app;
    public abstract class ActivityManagerNative extends Binder implements IActivityManager{
      static public IActivityManager getDefault() {
          return gDefault.get();
      }
      private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
          protected IActivityManager create() {
    //Getting AMS
              IBinder b = ServiceManager.getService("activity");
              if (false) {
                  Log.v("ActivityManager", "default service binder = " + b);
              }
              IActivityManager am = asInterface(b);
              if (false) {
                  Log.v("ActivityManager", "default service = " + am);
              }
              return am;
          }
      };
    }

(AMS)mgr.attachApplication()

package com.android.server.am;
public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
     @Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
//This method of executing AMS
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }

    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
        //...


        try {
                      ProfilerInfo profilerInfo = profileFile == null ? null
                    : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
//The bindApplication method of ApplicationThread is executed
            thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode,
                    mBinderTransactionTrackingEnabled, enableTrackAllocation,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());
            //.....
        } catch (Exception e) {
           //......
        } 
        // See if the top visible activity is waiting to run in this process...
        if (normalMode) {
            try {
                if (mStackSupervisor.attachApplicationLocked(app)) {
                    didSomething = true;
                }
            } catch (Exception e) {

            }
        }
        return true;
    }
}

Here we mainly look at two methods, thread.bindApplication(...) method and mStackSupervisor.attachApplicationLocked(app).
ApplicationThread is the internal class of ActivityThread, which mainly binds this ApplicationThread to AMS.
mStackSupervisor is an ActivityStackSupervisor class that is initialized when AMS is created.

mStackSupervisor.attachApplicationLocked(app)

Let's look at his approach.

package com.android.server.am;
public final class ActivityStackSupervisor implements DisplayListener {
    boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
        final String processName = app.processName;
        boolean didSomething = false;
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                if (!isFocusedStack(stack)) {
                    continue;
                }
                ActivityRecord hr = stack.topRunningActivityLocked();
                if (hr != null) {
                    if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                            && processName.equals(hr.processName)) {
                        try {
//Looking at this method, you know that it's really starting an Activity.
                            if (realStartActivityLocked(hr, app, true, true)) {
                                didSomething = true;
                            }
                        } catch (RemoteException e) {
                            Slog.w(TAG, "Exception in new application when starting activity "
                                  + hr.intent.getComponent().flattenToShortString(), e);
                            throw e;
                        }
                    }
                }
            }
        }
        if (!didSomething) {
            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
        }
        return didSomething;
    }

    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {

//It's true, so it's bound to go in.
        if (andResume) {
//Freeze other apps that have not yet started
            r.startFreezingScreenLocked(app, 0);
            mWindowManager.setAppVisibility(r.appToken, true);
            // Collect information about slower start-up apps
            r.startLaunchTickingLocked();
        }


        final ActivityStack stack = task.stack;
        try {
            if (app.thread == null) {
                throw new RemoteException();
            }
            List<ResultInfo> results = null;
            List<ReferrerIntent> newIntents = null;
            if (andResume) {
                results = r.results;
                newIntents = r.newIntents;
            }
           //...

            if (r.isHomeActivity()){
//If it's a desktop activity, add it to the bottom of the stack
                mService.mHomeProcess = task.mActivities.get(0).app;
            }

        //. . . . 
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
        } catch (RemoteException e) {          
            }
        }

        return true;
    }
}

There are a lot of codes in the above method. The main thing is to check the information of Activity which is ready to start. If the information is normal, the app.thread.scheduleLaunchActivity(....) method is executed.
Then back to Application Thread:

ApplicationThread.scheduleLaunchActivity(.....)

package android.app;
public final class ActivityThread {
    private class ApplicationThread extends ApplicationThreadNative {
        @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
            updateProcessState(procState, false);
            ActivityClientRecord r = new ActivityClientRecord();
            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
        } 
   }
}

It creates an Activity Client Record to store all kinds of information about the activity, and then sends it to H in the message. The message is identified as H.LAUNCH_ACTIVITY, and the content of the message is the Activity Client Record.
H is also an internal class of ActivityThread:

ActivityThread.H

private class H extends Handler {
    public static final int LAUNCH_ACTIVITY         = 100;
    public void handleMessage(Message msg) {
        switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
              }
    }
}

When this handler receives information from LAUNCH_ACTIVITY, it calls the handleLaunchActivity(...) method

ActivityThread.handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
      //.....
        Activity a = performLaunchActivity(r, customIntent);
//.....
    }

ActivityThread.performLaunchActivity(r, customIntent)

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
   //Get information about activity
        ActivityInfo aInfo = r.activityInfo;
        //... make some judgments about information
        Activity activity = null;
        try {
//Get the activity class by reflection
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
//If you don't register in the list, you'll make this mistake. You've all met it.
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
//Get application
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
//Contentext for generating activity
                Context appContext = createBaseContextForActivity(r, activity);
               //...
//Binding activity to application
                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);
             //Slightly
//Call the callActivityOnCreate method
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }       
            }
        } catch (SuperNotCalledException e) {         
        }
        return activity;
    }

Continue

Instrumentation.callActivityOnCreate

package android.app;
public class Instrumentation {
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }
}

Did you finally see the way to activity? It's coming soon.

activity.performCreate(icicle);

package android.app;
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback {

    final void performCreate(Bundle icicle) {
        restoreHasCurrentPermissionRequest(icicle);
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }
}

See, the onCreate method has been implemented, and there are some lifecycle methods that do the same logic, including service and broadcasting.

Posted by engelsol on Sun, 14 Jul 2019 10:20:02 -0700