preface

First of all, I want to say that I think Activity 6, want to job interview, want to advance senior engineer, want to deeply understand Activity (interested in) students please look down, do not meet the matter, please collect, want to see and then point out research. Please fasten your safety belt, we are going to start.

— No picture,say a J8!


Start the process

The following parsing is based on SDK25

Activity

At this point, do you expect me to introduce activities? Wrong, straight to the point.

@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}Copy the code
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
    if(options ! = null) { startActivityForResult(intent, -1, options); }else{ startActivityForResult(intent, -1); }}Copy the code
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
    startActivityForResult(intent, requestCode, null);
}Copy the code
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        if(ar ! = null) { mMainThread.sendActivityResult( mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); }if (requestCode >= 0) {
            mStartedActivity = true;
        }
        cancelInputsAndStartExitTransition(options);
    } else {
        if(options ! = null) { mParent.startActivityFromChild(this, intent, requestCode, options); }else{ mParent.startActivityFromChild(this, intent, requestCode); }}}Copy the code

MParent == null fragment = mParent == null fragment The key method mInstrumentation. ExecStartActivity (), to return to the callback. Instrumentation to test the students are not strange, here and when it is a black technology tool.


public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { ... int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! = null ? target.mEmbeddedID : null, requestCode, 0, null, options); checkStartActivityResult(result, intent); . }Copy the code

Here are the first four parameters:

  • Who: The context in which the Activity is being started
  • ContextThread: the contextThread that is starting the Activity, ApplicationThread in this case
  • Token: Indicates that the Activity is being started
  • Target: The Activity that is starting the Activity, that is, the Activity that is calling back the result


Let’s look at the checkStartActivityResult method below.

public static void checkStartActivityResult(int res, Object intent) {
    if (res >= ActivityManager.START_SUCCESS) {
        return;
    }
    switch (res) {
        case ActivityManager.START_INTENT_NOT_RESOLVED:
        case ActivityManager.START_CLASS_NOT_FOUND:
            if(intent instanceof Intent && ((Intent)intent).getComponent() ! = null) throw new ActivityNotFoundException("Unable to find explicit activity class "
                        + ((Intent)intent).getComponent().toShortString()
                        + "; have you declared this activity in your AndroidManifest.xml?");
            throw new ActivityNotFoundException(
                    "No Activity found to handle " + intent);
        case ActivityManager.START_PERMISSION_DENIED:
            throw new SecurityException("Not allowed to start activity "
                    + intent);
        case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
            throw new AndroidRuntimeException(
                    "FORWARD_RESULT_FLAG used while also requesting a result");
        case ActivityManager.START_NOT_ACTIVITY:
            throw new IllegalArgumentException(
                    "PendingIntent is not an activity");
        case ActivityManager.START_NOT_VOICE_COMPATIBLE:
            throw new SecurityException(
                    "Starting under voice control not allowed for: " + intent);
        case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:
            throw new IllegalStateException(
                    "Session calling startVoiceActivity does not match active session");
        case ActivityManager.START_VOICE_HIDDEN_SESSION:
            throw new IllegalStateException(
                    "Cannot start voice activity on a hidden session");
        case ActivityManager.START_CANCELED:
            throw new AndroidRuntimeException("Activity could not be started for "
                    + intent);
        default:
            throw new AndroidRuntimeException("Unknown error code "
                    + res + " when starting "+ intent); }}Copy the code

Have you seen any of these anomalies? An exception is thrown based on the intent and return code. The most familiar exception is an activity that is not registered in the AndroidManifest. The key to it is not what we want to do today, in this method, we also found startActivity of familiar with word, but the caller was very strange ActivityManagerNative. GetDefault (), who the hell is this?

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; }}; static public IActivityManager asInterface(IBinder obj) { ...return new ActivityManagerProxy(obj);
}Copy the code

It looks as if the ServiceManager builds an object with the activity key that instantiates the creation singleton as an argument to ActivityManagerProxy and returns it with GET. Here do not make resolution first, continue to connect the process above ActivityManagerNative. GetDefault () startActivity (…). . We already know that the startActivity method is actually called by ActivityManagerProxy, so let’s look at it again.

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(); Parcel reply = Parcel.obtain(); . mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0); . reply.recycle(); data.recycle();return result;
}Copy the code

It’s great if you’re familiar with Transact, so skip to the next section. Binder was introduced, but why Android? Is ActivityManagerProxy a proxy class? Why, I don’t answer directly, if we want to communicate across the process service, do you create a xx.aidl file first, and then a XX.java file will be automatically generated. Have you read it carefully? If you haven’t, check it out. You’ll find striking similarities. If it was the same process, I’m sure no one would do that? Our Activity is the client, and our Service is the server. The same is true here. The proxy class is ActivityManagerProxy. The actual transmission of data is mremote.transact. Briefly, the first parameter code tells the server what method we are requesting, and the second parameter data is the data we are passing. The tricky question here is why do we cross processes? Starting an application initializes an init process, which in turn generates Zygote, a Zygote process, which forks the important SystemServer process, AMS, WMS, This is how the PMS is started, and our application process also relies on AMS communication to tell Zygote to fork out, so it needs to communicate across processes, by the way, to answer the above unanswered questions, ServiceManager builds our fabled AMS. Binder was introduced at a long length to help you understand Binder, but it is only a brief introduction and some details are not introduced. You can have a systematic understanding of Binder outside the text. It is very complicated.

Summary:

ActivityManagerService

It’s almost time to get to the topic of the day. I know you’re going to say MMP, but what’s the point of that in order to get a high salary?


@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,resulUserHandle.getCallingUserId());
}Copy the code

Aye? Compare the parameters, haha similar? That should be this, right? What does parameter mean? If you would like to open AS to find the execStartActivity method in the Instrumentation class, which we mentioned above, but many details are omitted, I will briefly introduce it here, otherwise it is too long.

  • Caller: Is our whoThread, see Instrumentation parameter analysis
  • CallingPackage: package name
  • Intent: intention
  • ResolvedType: is the MIME type registered with our AndroidManifest
  • ResultTo: is our token, see Instrumentation parameter analysis
  • ResultWho: is the ID of our target
  • RequestCode: Forget about that
  • StartFlags: indicates the start flag. The default value is 0
  • ProfilerInfo: Null by default
  • Options: skip

Due to the length of the text, the next parameter may not be carefully analyzed, please follow the direction of the parameter or the parameter name to understand. The AMS startActivity method does nothing but call the startActivityAsUser method.

@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);
    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

The enforceNotIsolatedCaller method checks whether an object is isolated. MUserController. HandleIncomingUser check permissions and then return a user id (involving Linux, interested students can browse through, this is not our focus, startActivityAsUser method is detecting the permissions, It then returns the startActivityMayWait method called by the mActivityStarter(the ActivityStarter, so to speak, has all the information about how the Activity starts, and then assigns the Activity to the specific task stack based on that information).


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) { ... ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId); . ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo); . final ProcessRecord heavy = mService.mHeavyWeightProcess;if(heavy ! = null && (heavy.info.uid ! = aInfo.applicationInfo.uid || ! heavy.processName.equals(aInfo.processName))) { ... }... 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); . }Copy the code

Oh my God, there is another thing, MMP, can you analyze it? The mSupervisor(ActivityStackSupervisor) is simply used to manage ActivityStack. The resolveIntent and resolveActivity methods are used to determine which activity is launched. About the heavy (ResolveInfo) involves the heavyweight process (SystemServer MediaServer, ServiceManager), if already exist in the current system of heavyweight process and this is not about to start, then to Intent assignment. Next, continue our journey to startActivityLocked.

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) {
    ...
    ProcessRecord callerApp = null; . ActivityRecordsourceRecord = null; ActivityRecord resultRecord = null; . ActivityRecord r = new ActivityRecord(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); .return err;
}Copy the code

Another long way to go, drink a cold coke and keep going.


private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
    setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession, voiceInteractor); computeLaunchingTaskFlags(); . mIntent.setFlags(mLaunchFlags); mReusedActivity = getReusableIntentActivity(); . boolean newTask =false; .if(mStartActivity.resultTo == null && mInTask == null && ! mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) ! = 0) { newTask =true; . }... mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions); . mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity, mOptions); . }Copy the code

This is a relatively important method, but it’s not the big deal, so let’s look at setInitialState,

private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
            boolean doResume, int startFlags, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
    reset();
    mStartActivity = r;
    mIntent = r.intent;
    mOptions = options;
    mCallingUid = r.launchedFromUid;
    mSourceRecord = sourceRecord; mVoiceSession = voiceSession; . mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP; mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE; mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK; mLaunchFlags = adjustLaunchFlagsToDocumentMode( r, mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags()); . }Copy the code

The more important mStartActivity, mIntent, mCallingUid, mSourceRecord, and mLaunchFlags are initialized. MLaunchFlags is used to record how our Activity is started. All the omitted parts are used to initialize a bunch of variables or perform operations based on how our Activity is started. The following method computeLaunchingTaskFlags is used to initialize the startup logo, my god! Set minten. setFlags(mLaunchFlags) after initializing the boot mode flag bit. After initial startup mode, then we need to initialize the Tack, if you have any understanding on the startup mode of students here are better understood, in getReusableIntentActivity approach, we need to find a reusable ActivityRecord, Only SingleInstance can be reused, since there is only one instance in the entire system. This is also a case where the same Activity can be started next to each other. Why? Interested students can try (mainly test the understanding of priming mode). The startActivityUnchecked methods took three days and nights to detail. Continue to look at the following methods.

final void startActivityLocked(ActivityRecord r, boolean newTask, boolean keepCurTransition,
            ActivityOptions options) {
    ...
    mWindowManager.setAppVisibility(r.appToken, true); . }Copy the code

God, could not help but burst out a rough mouth, finally see WMS, not far from the day of prison, to TM’s high salary, I write XML on the line, so many requirements.





ActivityThread

Finally calling the scheduleLaunchActivity method of ApplicationThread!

@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

And finally, that familiar flavor, handler sends a message, so what is this handler? Welcome number one Handler, H.

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

We look directly at the message we sent, which calls the handleLaunchActivity method of the ActivityThread.

WindowManagerGlobal.initialize(); // Initialize WMS Activity a = performLaunchActivity(r, customIntent); // Create and startCopy the code
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; // Retrieve the component information and proceed with the operation... Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); // Start creating an instance of the Activity using the class loader. Application app = r.packageInfo.makeApplication(false, mInstrumentation); // Get 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); // Create an association with window... mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); //callActivityOnCreate->activity.performCreate->onCreate... }}Copy the code
public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
    if(mApplication ! = null) {returnmApplication; } // If mApplication is not empty, it is returned directly, which is why Application is a singleton... Application app = null; . ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); // Create an Application, like an Activity, using the class loader to create... mApplication = app; // Save... instrumentation.callApplicationOnCreate(app); //callApplicationOnCreate->onCreate ... }Copy the code

Summary:

conclusion





conclusion

Here, there is a sense of the end of the college entrance examination. I’m going to pit a handful of king pesticide. No, chicken. Uh, a few. Without further discussion, today’s process is just a main process, and the ideal process may actually be over halfway. Although the late ANALYSIS of AMS, omission is indeed a bit excessive.



here


portal

Github: github.com/crazysunj Blog crazysunj.com/