preface

From the previous article (Binder application Layer Summary and Analysis), you saw an overview of interprocess communication, such as the Activity launch process described today, with Binder. The system wraps a lot of work around this process, making it easy to start an Activity. This seemingly simple fact is actually the result of multiple communications between the Activity and ActivityManagerService. Read the article suggested with the source code to eat together, taste better.

Launcher

The mobile desktop is also an App, and the icon of each application is listed on the Launcher. Clicking the icon triggers the onItemClick event. For example, if we want to start the App “Taobao”, we should first define the default Activity information in the manifest file.

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>Copy the code

Then Launcher gets this information and launches the Taobao App

// The package name of the application String PKG = info.activityInfo.packageName;// The application's main activity class Stringcls = info.activityInfo.name;

            ComponentName componet = new ComponentName(pkg, cls);

            Intent i = new Intent();
            i.setComponent(componet);
            startActivity(i);Copy the code

Starting an Activity is managed by the Activity manager ActivityManagerService (AMS), whether it is two different activities in the same application, or different applications in different processes. The communication between them needs to use Binder, which communicates with AMS for many times to launch taobao App.

The whole process

Through the study of Android operating system can improve our understanding of operating system in the implementation of technology, which is very helpful to strengthen the internal power of developers. However, since most of the internal implementation of Android is more complex, the study of internal implementation should focus more on the overall process grasp, and can not go into the details of the code.

  1. The Launcher notifies AMS to start the MainActivity of the Taobao APP, which is the Activity that the manifest file sets to start.
  2. AMS records information about the Activity to start and tells the Launcher to enter the pause state.
  3. When the Launcher enters the pause state, AMS is notified that Paused has been executed and Taobao can be started.
  4. Taobao app has not been opened, so AMS starts a new process and creates an ActivityThread object in the new process, which executes the main function method.
  5. After the main thread of Taobao APP is started, notify AMS and pass in applicationThread for communication.
  6. AMS notifies Taobao to bind Application and start MainActivity.
  7. Taobao launches MainActivitiy, creates and associates the Context, and calls the onCreate method.

startActivityForResult

Let’s start with the Activity’s startActivity method. There are several ways to overload the startActivity method, but they all end up calling the startActivityForResult method.

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if(options ! =null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1); }}Copy the code

Within the startActivityForResult method, the execStartActivity method of the Instrumentation is called.

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {

            ......

            Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); . }Copy the code

Instrumentation

Instrumentation literally means Instrumentation, specific to the program is a tool class to manage activity, including creating and starting activity, activity lifecycle methods are controlled by Instrumentation this instrument, Only one Instrumentation instance is used in a process.

    / * * * * * @param who The Context from which the activity is being started.
     * @param contextThread The main thread of the Context from which the activity
     *                      is being started.
     * @param token Internal token identifying to the system who is starting 
     *              the activity; may be null.
     * @param target Which activity is performing the start (and thus receiving 
     *               any result);
     *               may be null if this call is no`t being made form an activity.
     * @param intent The actual Intent to start.
     * @param requestCode Identifier for this request's result; less than zero 
     *                    if the caller is not expecting a result.
     * @param options Addition options.
     *
     */
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, intrequestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread; .try {
            intresult = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! =null ? target.mEmbeddedID : null,
                        requestCode, 0.null, options);

            // Check the result of starting the Activity (throws exceptions, such as unregistered Activity in the manifest file)
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }Copy the code

We intercept the more critical code fragments to analyze the Instrumentation execStartActivity method, method parameter annotation also has a simple description of several parameters of the method. Let’s examine the two most important parameters, contextThread and Token.

IBinder contextThread

In a method on the incoming to mMainThread getApplicationThread () we can see that this is an IBinder object, shows that its role is the Binder used for interprocess communication object.

MMainThread is actually an ActivityThread object. The ActivityThread, which is the main thread, the UI thread, is created when the App starts, and it represents the App application. What? The ActivityThread represents the App Application. In fact, Application may be very important to us App developers, but in Android system is really not so important, it is a context. Doesn’t an Activity have a Context? The Application is the context for the entire ActivityThread.

We find the ActivityThread file. The getApplicationThread method actually gets the inner class ApplicationThread object and ApplicationThread inherits ApplicationThreadNative

public final class ActivityThread {// No other classes are inherited or implemented.

    final ApplicationThread mAppThread = new ApplicationThread();

    public ApplicationThread getApplicationThread()
    {
        return mAppThread;
    }

    //ActivityThread's inner class ApplicationThread
    private class ApplicationThread extends ApplicationThreadNative {. }}Copy the code

We open the ApplicationThreadNative class (available from the SDK source, The path is Sdk\sources\ Android-25 (Sdk version)\ Android \app\ApplicationThreadNative) ApplicationThreadNative is the AIDL communication Stub (server). ApplicationThreadProxy is the AIDL communication Proxy (client). See AIDL II for details. So ApplicationThread is the concrete implementation class for communication. As mentioned above, the Activity is actually started as a result of multiple interprocess communications, which leads us to conclude that ActivityThreads communicate with each other through the internal class ApplicationThread

public abstract class ApplicationThreadNative extends Binder implements IApplicationThread {

    static publicIApplicationThread asInterface(IBinder obj) {... } @Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, intflags) throws RemoteException {... }public IBinder asBinder(){return this;}

}

class ApplicationThreadProxy implements IApplicationThread {
    private final IBinder mRemote;

    publicApplicationThreadProxy(IBinder remote) { mRemote = remote; }... }Copy the code
public interface IApplicationThread extends IInterface {. }Copy the code
IBinder token

Traceback to the parameter origin, this token object is passed in the Attach method of the Activity, which is the Activity information passed in when the Activity is created and associated (as described below). This is also a Binder object that represents the Activity Launcher, which is also sent to AMS through Instrumentation. AMS can query it and know who sent the request to AMS.

//Activity

    private IBinder mToken;

    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) {

        mToken = token;
}Copy the code

The contextThread and token parameters are foreshadows that are passed to AMS. When AMS wants to send a message to the Launcher, it can use these two parameters to find the Launcher.


startActivity

In Instrumentation, launch the Activity the real implementation is by ActivityManagerNative getDefault startActivity () method to complete.

int result = ActivityManagerNative.getDefault(a).startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! = null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);Copy the code

ActivityManagerService (AMS) is derived from ActivityManagerNative (AMN), which is derived from Binder and implements the IActivityManager interface. AMS is a Binder, therefore, it is the concrete implementation IActivityManager, because ActivityManagerNative. GetDefault () is actually a IActivityManager type of Binder object, So its concrete implementation is AMS.

Let’s take a look at the code and go through the above relationships.

public final class ActivityManagerService extends ActivityManagerNative {. }public abstract class ActivityManagerNative extends Binder implements IActivityManager{. }public interface IActivityManager extends IInterface {. }Copy the code

As we know from our AIDL analysis, stubs hold Binder native objects and proxies hold Binder Proxy objects. So system naming is also based on this factor (Native) as the naming norm.

    /** * Retrieve the system's default/global activity manager. */
    static public IActivityManager getDefault() {
        return gDefault.get();
    }

    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            IActivityManager am = asInterface(b);
            returnam; }};/** * Cast a Binder object into an activity manager interface, generating * a proxy if needed. */
    static public IActivityManager asInterface(IBinder obj) {
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if(in ! =null) {
            return in;// Same process, return Stub local object.
        }

        return new ActivityManagerProxy(obj);// Across processes, return the proxy object.
    }Copy the code

It can be found that in AMN, AMS, the Binder object, is provided externally in Singleton mode. Singleton is a Singleton encapsulation class. When its GET method is called for the first time, it initializes AMS through create method. Subsequent calls directly return the previously created object (using the same AMS).

By analyzing the Create method, Binder analysis above shows that in IActivityManager (equivalent to Client), Binder reference 0 in the application can be obtained from SMgr for the Server. AMN uses getDefault to get a Binder reference object for AMS from ServiceManager and convert it into an ActivityManagerProxy object (AMP), which is the AMS proxy object.

Similar to the client binding code in AIDL, we can now communicate with AMS through ActivityManagerProxy (asInterface returns IActivityManager).

//ActivityManagerProxy

    public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {

        Parcel data = Parcel.obtain(a); / / fromParcelIn the pool to getParcelObject (communication carrier) used to write data
        Parcel reply = Parcel.obtain(); // If the method has a return value, write the return valuedata.writeInterfaceToken(IActivityManager.descriptor); //BinderA unique identifier// contextThread (contextThread); // contextThread (contextThread);AMSI can connect it toLaunchercommunicationdata.writeStrongBinder(caller! =null ? caller.asBinder(a) : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);//resultTo is the token mentioned aboveLaunchertheActivityinformationdata.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);. // Send type isSTART_ACTIVITY_TRANSACTIONThe request toAMS.datacontainsLauncherAnd taobaoAppThe information of
        mRemote.transact(START_ACTIVITY_TRANSACTION.data, reply, 0);
        reply.readException();

        //InstrumentationResult to check and throw an exception (ActivityInt result = reply.readint (); reply.recycle();data.recycle(a);
        return result;
    }Copy the code



Amn.getdefault () is actually AMS, so the starting process of the Activity is moved to AMS. Just look at AMS’s startActivity method.

  1. The Launcher notifies AMS to start the MainActivity of the Taobao APP, which is the Activity that the manifest file sets to start.









AMS

In the previous figure, we first summarized the overall process of Activity startup in an easy-to-understand way, and had a general understanding of the overall framework.




To view a larger version

AMS analysis

Again, by looking at the AMS class, we know that AMS is also a Binder and that it is a concrete implementation of IActivityManager.

public final class ActivityManagerService extends ActivityManagerNative {. }public abstract class ActivityManagerNative extends Binder implements IActivityManager{. }public interface IActivityManager extends IInterface {. }Copy the code

Then we continue to analyze the startActivity method of AMS. This stage is quite complicated and tedious, and we will get confused if we are not careful. We understand the general process without going into the details of the code, and we can establish enough understanding of the overall process. (Limited skill, after all)

//AMS

    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {

        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }


    @Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
        enforceNotIsolatedCaller("startActivity");
        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                userId, false, ALLOW_FULL_ONLY, "startActivity".null);

        //7.0Acitivty New ActivityStarter(originally ActivityStackSupervisor)
        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null.null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null.null, bOptions, false, userId, null.null);
    }Copy the code






ActivityStarter
//ActivityStarter

    final int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, IActivityManager.WaitResult outResult, Configuration config,
            Bundle bOptions, boolean ignoreTargetSecurity, int userId,
            IActivityContainer iContainer, TaskRecord inTask) {

            ` `` `` `

            // Select an appropriate activity from the system based on the intent.
            // The ResolverActivity pops up to let the user select the appropriate application.
            ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
                profileFile, profileFd, userId);

            ` `` `` `

            int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
                    aInfo, rInfo, voiceSession, voiceInteractor,
                    resultTo, resultWho, requestCode, callingPid,
                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                    options, ignoreTargetSecurity, componentSpecified, outRecord, container,
                    inTask);

            ` `` `` `

            return res;
    }



    // In startActivityLocked, verify the parameters passed in, and create an ActivityRecord object, Call again startActivityUnchecked startActivityUncheckedLocked is before (7.0) method to start the Activity.
    final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
            ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
            TaskRecord inTask) {
        int err = ActivityManager.START_SUCCESS;

        ` `` `` `
        // Create an ActivityRecord object
        //ActivityRecord: In AMS, ActivityRecord will be used as the recordkeeper for the Activity. Every Time an ActVityRecord is started, there will be an ActivityRecord object representing a record of the Activity
        ActivityRecord r = newActivityRecord(mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, requestCode, componentSpecified, voiceSession ! =null, mSupervisor, container,
                options, sourceRecord);

        ` `` `` `

        err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true, options, inTask);


        // This notifies the ActivityStarter that the Task for the Activity has been moved to the foreground
        postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack);

        return err;
    }Copy the code

In the startActivityLocked method, verify some of the parameters passed in, create an ActivityRecord object, and call the startActivityUnchecked method to start the Activity.

Understanding the startActivityUnchecked method, which is responsible for scheduling ActivityRecords and tasks, is key to understanding the Actvity startup pattern.

The startActivityUnchecked algorithm for scheduling tasks is complex, depending on the current stack rollback, the startup mode of the acitivity to be started, the taskAffinity attribute, and the intent flag that is set when the activity is started. The intent flags can vary, so the algorithm is complex and requires reading the source code and combining it with a specific startup situation to understand.

ActivityRecord is then added to the rollback stack by calling ActivityStack’s startActivityLocked

//ActivityStarter

    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {

            ``````
        //ActivityStack's startActivityLocked, don't get confused.
        // At the same time, call WindowManager to prepare the work related to App switch
        mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);


        if (mDoResume) {

            final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked();
            if(! mTargetStack.isFocusable() || (topTaskActivity ! =null&& topTaskActivity.mTaskOverlay && mStartActivity ! = topTaskActivity)) {// If the activity is not focusable, we can't resume it, but still would like to
                // make sure it becomes visible as it starts (this will also trigger entry
                // animation). An example of this are PIP activities.
                // Also, we don't want to resume activities in a task that currently has an overlay
                // as the starting activity just needs to be in the visible paused state until the
                // over is removed.

            } else {
                / / call ActivityStackSupervisor resumeFocusedStackTopActivityLocked finallymSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, mOptions); }}Copy the code






ActivityStackSupervisor
//ActivityStackSupervisor

    boolean resumeFocusedStackTopActivityLocked() {
        return resumeFocusedStackTopActivityLocked(null.null.null);
    }

    boolean resumeFocusedStackTopActivityLocked(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
        if(targetStack ! =null && isFocusedStack(targetStack)) {
        / / to start the corresponding Task to the foreground Task Activity, call the Task corresponding ActivityStack resumeTopActivityUncheckedLocked function
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }
        final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
        if (r == null|| r.state ! = RESUMED) {/ / otherwise you just call the front desk resumeTopActivityUncheckedLocked stack
            mFocusedStack.resumeTopActivityUncheckedLocked(null.null);
        }
        return false;
    }Copy the code






ActivityStack

Follow up with ActivityStack

//ActivityStack


    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {

        ` `` `` `
        result = resumeTopActivityInnerLocked(prev, options);

        return result;
    }


    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {


        //mResumedActivity points to the Activity Launcher that was launched last time.
        if (mResumedActivity ! = null) {
            ` `` `` `

           // Notify Launcher to enter the pause state
            pausing | = startPausingLocked(userLeaving, false.true, dontWaitForPause);
        }
         if (pausing) {//Launcher has paused
            ` `` `` `

            if (next.app ! = null && next.app.thread ! = null) {
                // If the app is already started
                // Call the priority of the process where the Taobao (to be started)Activity is located to ensure that it is not killed
                mService.updateLruProcessLocked(next.app, true.null); }}` `` `` `

        if (next.app ! = null && next.app.thread ! = null) {

        // If the Intent is not empty, call the NewIntent method
        next.app.thread.scheduleNewIntent(next.newIntents, next.appToken, false);

        // Assuming that the Taobao App is already started, click the Home button to go back to the Launcher and start Taobao from the Launcher again (or a third party to start the App already started)
        next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                        mService.isNextTransitionForward(), resumeAnimOptions);
        ` `` `` `

        } else {
        ` `` `` `
           // Create a process and cold start the Activity. Or if the App has been started, restart the Activity
           mStackSupervisor.startSpecificActivityLocked(next, true.true);
        }

        return true;
    }

Copy the code

ResumeTopActivityInnerLocked function is cumbersome, but overall, it should be only two of the more critical:

  1. Check whether an Activity(mResumedActivity) has started (that is, the Launcher that launches Taobao through the Launcher), and pause the Activity if it does
  2. Determine whether the target Activity needs to be restarted, that is, whether the Activity has already been started. (For example, save in the background, application switch)

2.AMS records the information about the Activity to start and sends a message to the Launcher to enter the pause state. 3.Launcher enters the pause state, and AMS is notified that Paused has been executed and Taobao can be started.







Create a process

The next steps are more important: create the process and start the Activity.

//ActivityStackSupervisor


    void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        if(app ! =null&& app.thread ! =null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {

                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
                // Restart the Activity if the target Activity's App is already started (there is an ActivityThread)
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
               ` `` `` `}}// If the process does not exist, use zygote to create an application process.
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true.0."activity", r.intent.getComponent(), false.false.true);
    }Copy the code

From the above code can learn startSpecificActivityLocked to start the Activity of two kinds of different situations

  • For example, to start Taobao cold from Launcher, you need to create a new process and incubate the application process through AMS calling Zygote.
  • If taobao App has been started, such as switching from MainActivity to LoginActivity, it passesrealStartActivityLockedStart.

Since we started with the Launcher taobao example, we went ahead and analyzed the AMS creation process and the Activity binding process. The above analysis to the mService startProcessLocked, here we direct way of starting a thread, in the middle of the process is a little complicated.

4. Taobao App has not been opened, so AMS starts a new process, creates ActivityThread object in the new process, and executes the main function method therein.

//ActivityServiceManager

        //Process.javaZygote will spawn a child Process that will call ActivityThread's main function Process via reflection.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);Copy the code

After the Zygote process incubates a new application process, the main method of the ActivityThread class is executed by reflection. In this method, Looper and message queue are prepared, the attach method is called to bind the application process to AMS, and a loop is entered to continuously read messages from the message queue and distribute messages.

//ActivityThread

public static void main(String[] args) {
    ` `` `` `
    // Prepare the main thread Looper, next post analysis Handler,Looper
    Looper.prepareMainLooper();

    // Create ActivityThread for the current process
    ActivityThread thread = new ActivityThread();

    // Bind the process to AMS
    thread.attach(false);

    if (sMainThreadHandler == null) {
    // Save the main thread Handler corresponding to the process
        sMainThreadHandler = thread.getHandler();
    }

    ` `` `` `
    // Enter the main thread message loop
    Looper.loop();

    ` `` `` `
}

As mentioned above, ApplicationThread is the intermediary that ActivityThread uses to communicate with AMS
final ApplicationThread mAppThread = new ApplicationThread();

private void attach(boolean system) {
    if(! system) {final IActivityManager mgr = ActivityManagerNative.getDefault();

        // Invoke the attachApplication method of AMS to bind the ApplicationThread object to ActivityManagerService
        // This allows AMS to control the application process through the ApplicationThread proxy object

            mgr.attachApplication(mAppThread);
    } else {
        ` `` `` `}}Copy the code

Now that the process has been created and the main thread is in place, all that is left to do is start the Activity and associate the context with initialization.

5. Notify AMS after the main thread of Taobao APP is started and pass in applicationThread for communication.






AMS Starts Activity summary

The following figure summarizes the general process of starting an Activity in AMS. Each method function is like different parts of a machine, each doing its duty and clearly dividing the work. It’s complicated, but not coupled. For example, the startup mode needs to be optimized by reworking the startActivityUnchecked method function.




Click to see a larger image







Associated with the Activity

At this point, even though you have the app process and the main thread, it’s still an empty shell. There is no activity information and no associated context. At this time, AMS should be asked to conduct the command.

The app at this time — >

Since the main thread sends the ApplicationThread to AMS via the attach method, the ApplicationThread bridge is used to inform the ActivityThread to create/associate and start the Activity.

//AMS


    @Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            // Get the process ID of the applicationThread.
            int callingPid = Binder.getCallingPid();
            final longorigId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); }}Copy the code

Obtain the process ID of proxy (ApplicationThread) through Binder, that is, obtain the Pid of target (Taobao) process.

//Binder


    /** * Return the ID of the process that sent you the current transaction * that is being processed. This pid can be used  with higher-level * system services to determine its identity and check permissions. * If the current thread is not currently executing an incoming transaction, * then its own pid is returned. */
    public static final native int getCallingPid(a);Copy the code

Next, we’ll focus on attachApplicationLocked methods

//AMS


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

        // Find the application record that is being attached... either via
        // the pid if we are running in multiple processes, or just pull the
        // next app record if we are emulating process with anonymous threads.
        ProcessRecord app;
        if(pid ! = MY_PID && pid >=0) {
            synchronized(mPidsSelfLocked) { app = mPidsSelfLocked.get(pid); }}else {
            app = null;
        }

        // Since the process is started by AMS, there must be ProcessRecord in AMS.
        // If ProcessRecord is not available, you need to kill the process and exit
        if (app == null) {
            ` `` `` `
            return false;
        }

        // If this application record is still attached to a previous
        // process, clean it up now.
        if(app.thread ! =null) {
            // If the IApplicationThread obtained from ProcessRecord is not empty, the IApplicationThread needs to be processed
            // It is possible that this Pid is reused, the old application process has just been released, and the internal IApplicationThread has not been emptied.
            // At the same time, the new process happens to use this Pid
            handleAppDiedLocked(app, true.true);
        }


        // Create dead proxy (notify AMS after process kill)
        AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);

        // The process was successfully registered. The timeout notification was removed
        mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

        ` `` `` `
        try {
            Binding Application / / * * * * * * * * * * * *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());

            updateLruProcessLocked(app, false.null);
        } catch (Exception e) {

            ` `` `` `
            // Restart the process after bindApplication fails
            startProcessLocked(app, "bind fail", processName);
            return false;
        }

        try {
            //****** Start the Activity(start taobao MainActivity)******
            if (mStackSupervisor.attachApplicationLocked(app)) {
                didSomething = true;//didSomething indicates whether there are four components to start}}catch (Exception e) {
            badApp = true;
        }

        ` `` `` `
        // Bind the Application of service and Broadcast


        if (badApp) {
            // If the above components fail to start, the process needs to be killed and the record removed
            app.kill("error during init".true);
            handleAppDiedLocked(app, false.true);
            return false;
        }

        DidSomething is false if none of the above components are started
        if(! didSomething) {// Adjust the oom_adj value of the process. Oom_adj is a kind of priority
            // If the application process has no components running, it is the first to be "killed" by the system when it runs out of memory
            updateOomAdjLocked();
        }
        return true;
    }Copy the code

There are two of the more important method functions in attachApplicationLocked, which brings us to the end of this article.

  1. Thread. BindApplication (…). Bind Application to ActivityThread
  2. MStackSupervisor. AttachApplicationLocked (app) : start the Activity (prior to 7.0 mMainStack. RealStartActivityLocked ())






bindApplication

Activitythreads communicate with AMS through applicationThreads, so thread.bindApplication (…) Method, which should be communicated through ApplicationThread. In the inner class ApplicationThread of ActivityThread, we find the bindApplication method

//ActivityThread

    // Inner class ApplicationThread
    private class ApplicationThread extends ApplicationThreadNative {

        public final void bindApplication(... A bunch of parameters...) { AppBindData data =new AppBindData();
            // Set parameters for data...`````` sendMessage(H.BIND_APPLICATION, data); }}private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        Message msg = Message.obtain();
        // Set parameters for MSG
        ``````
        mH.sendMessage(msg);
    }Copy the code

Sending messages is done through H’s Handler class, which is impressively named thief. PS: I suspect the engineers who wrote this class are running out of words…

    private class H extends Handler {
        public static final int LAUNCH_ACTIVITY         = 100;
        public static final int PAUSE_ACTIVITY          = 101; ` ` ` ` ` `public static final int RESUME_ACTIVITY         = 107;

        public static final int DESTROY_ACTIVITY        = 109;
        public static final int BIND_APPLICATION        = 110;
        public static final int EXIT_APPLICATION        = 111; ` ` ` ` ` `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; ` ` ` ` ` `/ / binding application
                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; }}}Copy the code

As you can see, this class H acts as the intermediary between ActivityThread and ApplicationThread, that is, the pimp. ActivityThread communicates with AMS through ApplicationThread. ApplicationThread communicates with the ActivityThread via H to process Activity transactions.

If Hand ApplicationThread are in the ActivityThread class, why should ApplicationThread send messages through Handler? In my opinion, it is convenient for centralized management and printing logs. H is the big steward of this.

Without further details, ApplicationThread sends the BIND_APPLICATION flag to H, where handleBindApplication handles application binding transactions.

//ActivityThread



        private void handleBindApplication(AppBindData data) {

           ` `` `` `
          // Create a LoadedApk object based on the ApplicationInfo passed
          data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);/ / get LoadedApk


          /** * For apps targetting Honeycomb or later, we don't allow network usage * on the main event loop / UI thread. This is what ultimately throws * {@link NetworkOnMainThreadException}. */
          // Disable network operations on the main thread
          if (data.appInfo.targetSdkVersion > = Build.VERSION_CODES.HONEYCOMB) {
              StrictMode.enableDeathOnNetwork();
          }
          /** * For apps targetting N or later, we don't allow file:// Uri exposure. * This is what ultimately throws {@link FileUriExposedException}. */
           / / 7.0 introduced Fileprovide
          if (data.appInfo.targetSdkVersion > = Build.VERSION_CODES.N) {
              StrictMode.enableDeathOnFileUriExposure();
          }

          ` `` `` `    
          // Create Android ContextImpl for the process
          final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);


          if ((InstrumentationInfo)ii ! = null) {
               ` `` `` `
          } else {
               // Note that all Activity lifecycle methods are monitored by Instrumentation objects,
               // This means that the Activity lifecycle method must call the related method of the Instrumentation object before and after execution
               mInstrumentation = new Instrumentation();
          }

          try {
             ` `` `` `
             Application app = data.info.makeApplication(data.restrictedBackupMode, null);
             mInitialApplication = app;


             // Load the process corresponding to the ContentProvider contained in the Package
             installContentProviders(app, data.providers);

             ` `` `` `        
             mInstrumentation.onCreate(data.instrumentationArgs);

             try {
                  // Application's onCreate method is called
                  // Therefore, the Applcation object's onCreate method is called after the ActivityThread's main method
                  // But will be called before all activities in the application
                  mInstrumentation.callApplicationOnCreate(app);
              } catch (Exception e) {
                  ` `` `` `
              }
            } finally {
                StrictMode.setThreadPolicy(savedPolicy); }}Copy the code

As mentioned above, the purpose of the Handlebinder Application is to integrate a Java process into the Android system. Therefore, the code in this function does the following:

  1. Set basic process parameters as required by Android, including process name, time zone, resources, and compatibility. Restrictions have also been added, such as the main thread not being able to access the network.
  2. Create ContextImpl, LoadedApk, Application, etc., load ContentProvider in Application, and initialize Application.
  3. useInstrumentationMonitor the Activity lifecycle. One process for eachInstrumentationInstance)

When this is done, the new process is finally added to the Android system.

6.AMS notifies Taobao to bind Application and start MainActivity.






Start the Activity

attachApplicationLocked

In the attachApplicationLocked method of AMS in the code above, we say:

There are two of the more important method functions in attachApplicationLocked, which brings us to the end of this article.

  1. Thread. BindApplication (…). Bind Application to ActivityThread
  2. MStackSupervisor. AttachApplicationLocked (app) : start the Activity

After binding Application, we can start the Activity(Taobao MainActivity).

//ActivityStackSupervisor


    boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
        final String processName = app.processName;
        boolean didSomething = false;

        / / ActivityStackSupervisor maintains all ActivityStack in the terminal
        // Find the Activity to start at the top of the foreground stack by polling
        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) {
                    // Start the foreground Activity when it matches the newly created process
                    if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                            && processName.equals(hr.processName)) {
                        try {

                            / / realStartActivityLocked actually start work
                            if (realStartActivityLocked(hr, app, true.true)) {
                                didSomething = true; }}catch (RemoteException e) {

                        }
                    }
                }
            }
        }

        return didSomething;
    }Copy the code

The system engineers probably feel that the startup process is confusing, and the final method is called realStartActivityLocked, which means don’t get confused, this is the method that will finally start the Activity. Finally, we go straight to huanglong, and in the ActivityStackSupervisor method, we find the following code

//ActivityStackSupervisor


            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);Copy the code

AMS uses ApplicationThread to tell ActivityThread to start the Activity. The ApplicationThread informs the ActivityThread to start the Activity. The ApplicationThread informs the ActivityThread to start the Activity. ApplicationThread — > H — > ActivityThread — > the method that finally starts the Activity.






ActivityThread
//ActivityThread


    // Inner class ApplicationThread
    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();
            // Set parameters` ` ` ` ` `// As you can see from the LAUNCH_ACTIVITY identifier, it is used to start the ActivitysendMessage(H.LAUNCH_ACTIVITY, r); }}private class H extends Handler {` ` ` ` ` `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;

                    // Use ApplicationInfo and other information to get the corresponding LoadedApk, save to ActivityClientRecord
                    //ActivityClientRecord contains activity-related information
                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null."LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break; ` ` ` ` ` `}}}Copy the code
//ActivityThread


private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ` `` `` `

    Activity a = performLaunchActivity(r, customIntent);
    if(a ! =null) {
        ` `` `` `
        handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed, r.lastProcessedSeq, reason);` `` `` `
    } 
    ` `` `` `
}Copy the code

The handleLaunchActivity method has two important function calls,

  • PerformLaunchActivity: invokes the Activity’s onCreate, onStart, onResotreInstanceState method
  • HandleResumeActivity: Calls the Activity’s onResume method.

As you can see from the source code above, the performLaunchActivity method finally completes the Activity object creation and launch process, And the ActivityThread calls the onResume lifecycle method of the initiated Activity through the handleResumeActivity method.






performLaunchActivity

This method does several things. [From Android Development Art Exploration]

1. Get the component information for the Activity to be started from the ActivityClientRecord.

//ActivityThread performLaunchActivity method ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent(a);
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity! = null) { component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }
Copy the code

2, through the newActivity method of Instrumentation using the class loader to create an Activity object.

ActivityThread performLaunchActivity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader(a);
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess(a);
            if (r.state! = null) { r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            ``````
        }Copy the code

LoadedApk makeApplication creates the Application object.

// The performLaunchActivity method of ActivityThread


Application app = r.packageInfo.makeApplication(false, mInstrumentation);//r.packageInfo is the LoadedApk objectCopy the code

In bindApplication, we explained how to create an Application with LoadedApk, and after that, Call the onCreate method of Application through the Instrumentation callApplicationOnCreate

 Application app = data.info.makeApplication(data.restrictedBackupMode, null);

    ` `` `` `
 mInstrumentation.callApplicationOnCreate(app);Copy the code

So the third step is to determine if the Application is empty, and we can see from the makeApplication method that if the Application has already been created, it won’t be created again.

4. Create ContextImpl and initialize some important data using the Attach method of the Activity.

                Context appContext = createBaseContextForActivity(r, activity); // Create ContextImpl object

                ``````
                Window window = null;
                if (r.mPendingRemoveWindow! = null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                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);Copy the code

ContextImpl is an important data structure that is the implementation of the Context. Most of the logic in the Context is done by ContextImpl. ContextImpl. ContextImpl is a ContextImpl attached to the Activity using the attach method of the Activity. In addition, in the attach method, the Activity completes the creation of the Window and associates itself with the Window. This allows the Window to pass the event to the Activity when it receives an external input event.

5. Call the Activity’s onCreate method

                mInstrumentation.callActivityOnCreate(activity, r.state);Copy the code

Since the Activity’s onCreate has already been called, this also means that the Activity has completed the entire startup process.

6, call the Activity onStart, onResotreInstanceState method

        mInstrumentation.callActivityOnCreate(activity, r.state);

        ` `` `` `

        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);Copy the code






conclusion

So far, the whole process of starting an Activity from the Launcher has been described. Through this analysis, we have a general control of the origin and origin of starting an Activity, but there are also several knowledge points that have not been explored clearly. (The more you explore, the more you find out.)

  • Looper, How Handler works (Android messaging)
  • What exactly is Context
  • What about Windows’ internal mechanics

What if taobao MainActivity starts LoginActivity? Matter of fact, broadly in line with a principle and the process in ActivityStackSupervisor startSpecificActivityLocked method, found that the process has started, direct call realStartActivityLocked start-up Activity.




Click to see a larger image

Refer to the “Android Development Art Exploration” Activity startup process series

Start the Activity (top) Start the Activity (Bottom)

General overview of Jianqiang Bao’s startup process (II) (III) (IV)

Diagram the Application startup process