Follow up the Activity Start process (1) : ActivityManagerService section

Last time we looked at the process of launching an activity in the AndroidManagerService section, this time we’ll look at the process of launching an activity in the application process section. Learning the startup process of an activity in the application process section is helpful for us to gain a deeper understanding of how an Android application works, especially to learn some knowledge about plugins, because most of the startup process described in the previous article is in AMS, which is a system service process that we cannot modify. The startup process introduced in this paper is located in the application process, and we can use reflection and other technical means to modify it. The core principle of some plug-in technologies is to hook the code in this part to achieve the purpose of replacing the activity to be started.

1. Distribute the event that starts a new activity

As you can see at the end of the previous article, AMS finally calls the scheduleLaunchActivity method of ActivityThread, and the activity starts from the AMS process to the application process:

            app.thread.scheduleLaunchActivity(newIntent(r.intent), r.appToken, System.identityHashCode(r), r.info, mergedConfiguration.getGlobalConfiguration(), mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, ! andResume, mService.isNextTransitionForward(), profilerInfo);Copy the code

Let’s look at the scheduleLaunchActivity method in ActivityThread:

Source path: \frameworks\base\core\ Java \android\app\ activityThread.java@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);
    }
Copy the code

In the scheduleLaunchActivity method, the received parameters are wrapped into an ActivityClientRecord object. As the name suggests, an ActivityClientRecord is a record of an Activity on the client side, encapsulating information about the activity.

The sendMessage method is called at the end of the scheduleLaunchActivity method to send a message. Let’s look at the sendMessage method:

    private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0.0.false);
    }

    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + "" + mH.codeToString(what)
            + ":" + arg1 + "/" + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }
Copy the code

As you can see, the sendMessage method builds a Message object and sends it through the mH object. MH is obviously a Handler object, which uses Android’s messaging mechanism for message distribution. MH is an instance of the class named H, which inherits from Handler. The mH is used to handle events in the main thread. Using the messaging mechanism to start an activity ensures that the activity starts in the main thread. The message is eventually processed by the mH handleMessage method, which has the following source code:

    private class H extends Handler {

        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            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; . }... }}Copy the code

In the handleMessage method, getPackageInfoNoCheck is first called and the return value is assigned to the packageInfo variable of R. GetPackageInfoNoCheck returns a LoadedApk object that encapsulates apK-related information. The system then calls the handleLaunchActivity method to further launch the activity.

Look at the code for the getPackageInfoNoCheck method:

    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
            boolean registerPackage) {
        final booleandifferentUser = (UserHandle.myUserId() ! = UserHandle.getUserId(aInfo.uid));synchronized (mResourcesManager) {
            WeakReference<LoadedApk> ref;
            if (differentUser) {
                ref = null;
            } else if (includeCode) {
                // Note 1: Check whether there is a cache
                ref = mPackages.get(aInfo.packageName);
            } else{ ref = mResourcePackages.get(aInfo.packageName); } LoadedApk packageInfo = ref ! =null ? ref.get() : null;
            if (packageInfo == null|| (packageInfo.mResources ! =null
                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                ...
                // Note 2: Create a new LoadedApk object if there is no cache
                packageInfo =
                    new LoadedApk(this, aInfo, compatInfo, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) ! =0, registerPackage); .if (differentUser) {
                    // Caching not supported across users
                } else if (includeCode) {
                    // Note 3: Put the LoadedApk object in the cache
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            newWeakReference<LoadedApk>(packageInfo)); }}returnpackageInfo; }}Copy the code

First, in comment 1, try to get the LoadedApk object from the mPackages object. If not, then create a new LoadedApk object in comment 2 and add the LoadedApk to the mPackages object in comment 3.

Let’s look at the code for the handleLaunchActivity method again:

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

        // Note 1: Initialize WindowManager and obtain WindowServiceManager's local agent through Binder
        WindowManagerGlobal.initialize();

        // Note 2: Create an Activity
        Activity a = performLaunchActivity(r, customIntent);

        if(a ! =null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            / / comment 3
            handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed, r.lastProcessedSeq, reason); . }... }Copy the code

In note 1 place, by calling the WindowManagerGlobal. The initialize () to the WindowManager initialized, WindowManger WindowManagerService in a proxy object for the client, WindowManagerService is a system service similar to ActivityManagerService, which is mainly used to control the display of Windows. Clients communicate with WindowManager Services using Binder mechanisms through WindowManager objects.

The Activity instance we want to launch is finally created by creating an instance of the Activity with performLaunchActivity in comment 2.

At comment 3, the handleResumeActivity method is called to put the activity into the resume state.

Let’s take a look at what the performLaunchActivity and handleResumeActivity do to initialize the activity.

2. Activity initialization

The source code for the performLaunchActivity method is as follows:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {... ComponentName component = r.intent.getComponent(); .// Note 1: Create a context for the activity
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();

            // Note 2: Create an activity instanceactivity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); . }...try{...if(activity ! =null) {... appContext.setOuterContext(activity);/ / comment 3
                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, r.configCallback); . checkAndBlockForNetworkAccess(); activity.mStartedActivity =false;
                // Set the theme
                int theme = r.activityInfo.getThemeResource();
                if(theme ! =0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                // Note 4: Call the OnCreate method on the activity
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if(! activity.mCalled) {throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
                r.stopped = true;
                if(! r.activity.mFinished) {// Note 5: Start state
                    activity.performStart();
                    r.stopped = false; }... } r.paused =true; mActivities.put(r.token, r); }...return activity;
    }
Copy the code

In note 1, the system first creates a context object for the activity and uses this context to obtain a ClassLoader to load the activity to be started.

At note 2, an instance of the activity is created by calling the mInstrumentation newActivity method.

The Attach method of the activity is called in note 3 to do some initialization of the activity.

The Minstrumumentation callActivityOnCreate method is called at note 4, which, as you can see from the method’s name, internally calls the activity’s onCreate method.

The activity’s performStart() method is called at comment 5 to put the activity into the start state.

Let’s first look at the mInstrumentation newActivity method:

Source path: \frameworks\base\core\ Java \ Android \app\Instrumentation.java

    public Activity newActivity(ClassLoader cl, String className, Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }
Copy the code

The newActivity method is a single line of code that loads the activity class to be started and creates an instance object using reflection.

Let’s take a look at what initialization the activity attach method does:

Source path: \frameworks\base\core\ Java \ Android \app\Activity.java

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        // Note 1: Create a Window object for the activity
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        // Set the softkeyboard status mode
        if(info.softInputMode ! = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); }if(info.uiOptions ! =0) {
            mWindow.setUiOptions(info.uiOptions);
        }

        // Note 2: Initialize some member variables
        mUiThread = Thread.currentThread();
        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if(voiceInteractor ! =null) {
            if(lastNonConfigurationInstances ! =null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this.this, Looper.myLooper()); }}// Note 3: Set windowManager for WindowmWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! =0);
        if(mParent ! =null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);
    }
Copy the code

In note 1, we create a Window object for this activity. You can see that this Window object is actually a PhoneWindow object. In the Android system, each Activity corresponds to a Window object, which is mainly related to the display of the Activity window.

Note 2 initializes some member variables for the activity, such as the main thread, UI thread, application object, and so on.

The WindowManager object is set for window in note 3.

After attaching the activity method, let’s look at the callActivityOnCreate method in Instrumentation:

Source path: \frameworks\base\core\ Java \ Android \app\Instrumentation.java

    public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle, persistentState);
        postPerformCreate(activity);
    }
Copy the code

You can see that the callActivityOnCreate method calls activity.performCreate to further initialize the activity. The source code for the performCreate method is as follows:

Source path: \frameworks\base\core\ Java \ Android \app\Activity.java

    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        restoreHasCurrentPermissionRequest(icicle);
        // Note 1: Call onCreate
        onCreate(icicle, persistentState);
        mActivityTransitionState.readState(icicle);
        / / comment 2
        performCreateCommon();
    }
Copy the code

The onCreate method is called directly in comment 1. We usually override an activity’s onCreate method to do some custom initialization. The performCreateCommon method is called again in comment 2. Let’s look at the source code for this method:

    final void performCreateCommon(a) { mVisibleFromClient = ! mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay,false);
        mFragments.dispatchActivityCreated();
        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
    }
Copy the code

You can see that the performCreateCommon method calls the dispatchActivityCreated method on mFragments. This method internally notifies the relevant fragment in the activity, And triggers the onActivityCreated method on these fragments.


Go back to the handleLaunchActivity method of the ActivityThread. After analyzing the performLaunchActivity method, let’s take a look at what the handleResumeActivity method does. The handleResumeActivity method code looks like this:

Source path: \frameworks\base\core\ Java \ Android \app\ActivityThread.java

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ActivityClientRecord r = mActivities.get(token); ./ / comment 1
        r = performResumeActivity(token, clearHide, reason);

        if(r ! =null) {
            finalActivity a = r.activity; .if (r.window == null&&! a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; . }else if(! willBeVisible) ... cleanUpPendingRemoveWindows(r,false /* force */);

            if(! r.activity.mFinished && willBeVisible && r.activity.mDecor ! =null&&! r.hideForNow) { ... WindowManager.LayoutParams l = r.window.getAttributes(); . }...// The activity is resumed through the Activity Manager
            if (reallyResume) {
                try {
                    ActivityManager.getService().activityResumed(token);
                } catch (RemoteException ex) {
                    throwex.rethrowFromSystemServer(); }}}else{... }}Copy the code

The code for the handleResumeActivity method is quite long, but we don’t have to get bogged down in the code details. Note 1 calls performResumeActivity. The following code is related to window, decorView, and windowManager operations. It is not difficult to guess that it is related to the activity display. Let’s focus on the performResumeActivity method:

    public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        if (localLOGV) Slog.v(TAG, "Performing resume of " + r
                + " finished=" + r.activity.mFinished);
        if(r ! =null && !r.activity.mFinished) {
            ...
            try{... r.activity.performResume(); . }... }return r;
    }
Copy the code

The performResumeActivity method basically calls the activity’s performResume method:

    final void performResume(a) {...// Call the onResume method of the activity
        mInstrumentation.callActivityOnResume(this); .// Tell the fragment to enter the Resume statemFragments.dispatchResume(); mFragments.execPendingActions(); . }Copy the code

First, the system calls minstrumumentation’s callActivityOnResume. Inside the callActivityOnResume method, the activity’s onResume method will be called directly, much like the onCreate method. I won’t go any further here. The activity enters the Resume phase of its lifecycle.

conclusion

Let’s summarize this part of the activity launch process:

  1. AMS calls the scheduleLaunchActivity method of ActivityThread to start the process from AMS into the application process
  2. An ActivityThread distributes events that start an activity through the messaging mechanism, ensuring that the activity starts in the main thread.
  3. Create an activiy instance using reflection and do some initialization for that instance
  4. The activity begins its life cycle by calling the onCreate, onStart, and onResume methods, and is displayed on the screen.

So far the activity of the creation of the process is finished, my level is limited, if there is a bad write also hope you big guy to give advice. The application process creation part of the startup process is not covered here, because it is too much, and I will write a separate article on the application process creation process.