Start the activity process

Time:2020-1-14

Preface

This is the fifth article in the Android 9.0 AOSP series. Let’s review the general contents of the previous articles.

Pangu and Nuwa in Java World — zygote

It mainly introduces the first java process in Android worldZygoteStart up process of.

  • Register server socket to respond to client requests
  • Various preloading operations, classes, resources, shared libraries, etc
  • Force GC once
  • Fork systemserver process
  • Loop to wait for socket request from client (request socket connection and request fork application process)

The eldest son of the zygote family — system server

This paper mainly introduces the first process of the zygote process forkSystemServerIt carries the creation and start-up of various system services.

  • Language, time zone, region and other settings
  • Virtual machine memory settings
  • Fingerprint information, binder call settings
  • Looper.prepareMainLooper(), create the main thread looper
  • Initialize native service, loadlibandroid_servers.so
  • createSystemContext(), initialize system context
  • Create system service managerSystemServiceManager
  • startBootstrapServices, start the system boot service
  • startCoreServices, start system core services
  • startOtherServices, start other services
  • Looper.loop(), turn on message loop

staystartOtherServicesWill call AMSonSystemReady()Method to start the desktop activity.

In Android world, who wakes up zygote?

This paper mainly introduces the process of AMS requesting zygote to create application process, that is, socket communication to zygote process, which corresponds to the first part.

  • callProcess.start()Create application process
  • ZygoteProcessResponsible andZygoteThe process establishes the socket connection, and sends the parameters needed for creating the process to the socket server of zygote
  • ZygoteAfter the server receives the parameters, it calls.ZygoteConnection.processOneCommand()Process parameters and fork process
  • Finally passedfindStaticMain()findActivityThreadThe main () method of the class is executed and the subprocess is started

“Ubiquitous” system core service — Analysis of activity manager service startup process

Mainly introducedActivityManagerService (AMS)It is closely related to the starting, switching, scheduling and application process management of the four components.

  • AMS initialization, viaActivityManagerService.LifecycleInitialization in the constructor of
  • setSystemProcess(), register various services, create processrecord, and update the value of oom? Adj
  • Install system provider
  • systemReady(), the desktop home activity will be launched eventually

Today, I want to introduce the startup process of activity. The launch of activity is a big project with a lot of details. This article will simply sort out the whole startup process, and will not go too deep into the source code details. The key issues, such as the handling of launchmode and life cycle, will be further analyzed in a separate article.

Start process analysis

First come to a flow chart, which is more convenient to understand.

Start the activity process

Then, in the previous analysis, the activity manager servicesystemReady()Method will finally start the desktop HME activity. The method called isstartHomeActivityLocked

> ActivityManagerService.java

boolean startHomeActivityLocked(int userId, String reason) {
    ......
    Intent intent = getHomeIntent();
    ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
    if (aInfo != null) {
        ......
        if (app == null || app.instr == null) {
            intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
            final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
            final String myReason = reason + ":" + userId + ":" + resolvedUserId;
            //Start desktop activity
            mActivityStartController.startHomeActivity(intent, aInfo, myReason);
        }
    } else {
        Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
    }
    return true;
}

callActivityStartControllerOfstartHomeActivity()Method:

> ActivityStartController.java

void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
        mSupervisor.moveHomeStackTaskToTop(reason);

        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
                .setOutActivity(tmpOutRecord)
                .setCallingUid(0)
                .setActivityInfo(aInfo)
                .execute();
        mLastHomeActivityStartRecord = tmpOutRecord[0];
        if (mSupervisor.inResumeTopActivity) {
            mSupervisor.scheduleResumeTopActivities();
        }
    }

obtainStarter()Method returnsActivityStarterObject, which is responsible for the start of activity, a series ofsetXXX()Method to pass in the various parameters required for startup, the lastexecute()It’s the real starting logic.

Before you continue to look at the source code, think about which process you are in now? AMS is insystem_serverIt is initialized in the process, so the above work issystem_serverProcess. And we usually use thestartActivity()Method is obviously called in the application process. So, ordinarystartActivity()What kind of call chain is the method? Follow-upActivity.startActivity()Let’s see how.

> Activity.java    

@Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            startActivityForResult(intent, -1);
        }
    }

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            //Call the instrumentation. Execstartactivity() method
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
              //Callback activityresult
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
        } else {
          //Finally, it calls the instrumentation. Execstartactivity() method
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

Will eventually callInstrumentationOfexecStartActivity()Method. Instrumentation is a very important class, which is indispensable for the start of activity and the callback of life cycle. This class will be encountered many times later.

> Instrumentation.java

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
             ......
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            //Binder calls AMS to start activity
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            //Test startup results
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

Here, we call AMS’sstartActivity()Method.ActivityManager.getService()You don’t have to think about getting AMS proxy objects.

> ActivityManager.java

public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

private static final Singleton<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @Override
            protected IActivityManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;
            }
        };

Then go to AMS’s startactivity () method.

> ActivityManagerService.java    

@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) {setMayWait
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }

    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,
            boolean validateIncomingUser) {
        enforceNotIsolatedCaller("startActivity");

        userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        // TODO: Switch to user app stacks here.
        Return mpactivitystartcontroller. Obtainstarter (intent, "startactivityasuser") // get the activitystarter object
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setMayWait(userId)
                .execute();

    }

Next, it is similar to the previous start of home activity. ObtainActivityStarterObject, providing parameters, and finallyexecute()

obtainStarter()Get the activitystarter object through factory mode.

    ActivityStarter obtainStarter(Intent intent, String reason) {
        return mFactory.obtain().setIntent(intent).setReason(reason);
    }

The default implementation of mfactory isActivityStarter.DefaultFactory

> ActivityStarter.java   

static class DefaultFactory implements Factory {
        /**
         * The maximum count of starters that should be active at one time:
         * 1. last ran starter (for logging and post activity processing)
         * 2. current running starter
         * 3. starter from re-entry in (2)
         *
         *A maximum of three starters can be activated at the same time.
         */
        private final int MAX_STARTER_COUNT = 3;

        private ActivityStartController mController;
        private ActivityManagerService mService;
        private ActivityStackSupervisor mSupervisor;
        private ActivityStartInterceptor mInterceptor;

        private SynchronizedPool<ActivityStarter> mStarterPool =
                new SynchronizedPool<>(MAX_STARTER_COUNT);

        DefaultFactory(ActivityManagerService service,
                ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
            mService = service;
            mSupervisor = supervisor;
            mInterceptor = interceptor;
        }

        @Override
        public void setController(ActivityStartController controller) {
            mController = controller;
        }

        @Override
        public ActivityStarter obtain() {
            //Get from synchronized pool
            ActivityStarter starter = mStarterPool.acquire();

            if (starter == null) {
                starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
            }

            return starter;
        }

        @Override
        public void recycle(ActivityStarter starter) {
            starter.reset(true /* clearRequest*/);
            mStarterPool.release(starter);
        }
    }

A synchronous object cache pool with a capacity of 3 is provided to cache activitystarter objects.setXXX()Methods are all parameter configurations. NotesetMayWaitMethod willmayWaitThe parameter is set to true. Let’s look directly at the actual implementation process,execute()Function.

> ActivityStarter.java       

int execute() {
        try {
            Set maywait to true in if (mrequest. Maywait) {// setmaywait() method
                return startActivityMayWait(mRequest.caller, mRequest.callingUid,
                        mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                        mRequest.inTask, mRequest.reason,
                        mRequest.allowPendingRemoteAnimationRegistryLookup,
                        mRequest.originatingPendingIntent);
            } else {
                return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
                        mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                        mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,
                        mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,
                        mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
                        mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
                        mRequest.outActivity, mRequest.inTask, mRequest.reason,
                        mRequest.allowPendingRemoteAnimationRegistryLookup,
                        mRequest.originatingPendingIntent);
            }
        } finally {
            //Recycle the current activitystarter object
            onExecutionComplete();
        }
    }

Next callstartActivityMayWait()

>  ActivityStarter.java

private 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, WaitResult outResult,
            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
            int userId, TaskRecord inTask, String reason,
            boolean allowPendingRemoteAnimationRegistryLookup,
            PendingIntentRecord originatingPendingIntent) {
       .....

        // Save a copy in case ephemeral needs it
        final Intent ephemeralIntent = new Intent(intent);
        // Don't modify the client's object!
        //Recreate without modifying the client's original intent
        intent = new Intent(intent);
        if (componentSpecified
                && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
                && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
                && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
                && mService.getPackageManagerInternalLocked()
                        .isInstantAppInstallerComponent(intent.getComponent())) {
            intent.setComponent(null /*component*/);
            componentSpecified = false;
        }

        //Get resolveinfo
        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                0 /* matchFlags */,
                        computeResolveFilterUid(
                                callingUid, realCallingUid, mRequest.filterCallingUid));
        ......
        // Collect information about the target of the Intent.
        //Get activityinfo of target intent
        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);

        synchronized (mService) {
            final ActivityStack stack = mSupervisor.mFocusedStack;
            stack.mConfigWillChange = globalConfig != null
                    && mService.getGlobalConfiguration().diff(globalConfig) != 0;
            ......

            final ActivityRecord[] outRecord = new ActivityRecord[1];
            //Call startactivity() method
            int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
                    ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
                    allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);

            Binder.restoreCallingIdentity(origId);

            ......

            if (outResult != null) {
                //Set startup results
                outResult.result = res;

                final ActivityRecord r = outRecord[0];

                switch(res) {
                    case START_SUCCESS: {
                        mSupervisor.mWaitingActivityLaunched.add(outResult);
                        do {
                            try {
                                //Wait for start result
                                mService.wait();
                            } catch (InterruptedException e) {
                            }
                        } while (outResult.result != START_TASK_TO_FRONT
                                && !outResult.timeout && outResult.who == null);
                        if (outResult.result == START_TASK_TO_FRONT) {
                            res = START_TASK_TO_FRONT;
                        }
                        break;
                    }
                     ......
                        break;
                    }
                }
            }

            return res;
        }
    }

transferstartActivity()Method, which has two overloaded methods called in turn. Wait for the startup result here.

>  ActivityStarter.java  

private int startActivity(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,
            SafeActivityOptions options,
            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
            PendingIntentRecord originatingPendingIntent) {
        int err = ActivityManager.START_SUCCESS;

        ProcessRecord callerApp = null;
        if (caller != null) {
            //When the caller is not empty, use AMS to find processrecord
            callerApp = mService.getRecordForAppLocked(caller);
            if (callerApp != null) {
                callingPid = callerApp.pid;
                callingUid = callerApp.info.uid;
            } else {
                err = ActivityManager.START_PERMISSION_DENIED;
            }
        }

        //Sourcerecord is used to describe the activity that initiates this request
        //Resultrecord user description activity receiving startup result
        //Generally, the two activities should be the same
        ActivityRecord sourceRecord = null;
        ActivityRecord resultRecord = null;
        ......
        //Get start flag
        final int launchFlags = intent.getFlags();

        ......

        if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
            //No class could be found to handle the intent
            err = ActivityManager.START_INTENT_NOT_RESOLVED;
        }

        if (err == ActivityManager.START_SUCCESS && aInfo == null) {
            //The activity class specified in intent was not found
            err = ActivityManager.START_CLASS_NOT_FOUND;
        }

        ......

        //Authority check
        boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
                requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
                inTask != null, callerApp, resultRecord, resultStack);
        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                callingPid, resolvedType, aInfo.applicationInfo);

        ......

        //Build activityrecord
        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
                resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
                mSupervisor, checkedOptions, sourceRecord);

        ......

        //Get the activitystack of the current focus
        final ActivityStack stack = mSupervisor.mFocusedStack;

        //If you start a new activity with a uid different from the activity currently in the resume state, check whether app switching is allowed
        if (voiceSession == null && (stack.getResumedActivity() == null
                || stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
            if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
                    realCallingPid, realCallingUid, "Activity start")) {
                mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
                        sourceRecord, startFlags, stack, callerApp));
                ActivityOptions.abort(checkedOptions);
                //Switch not allowed, return directly
                return ActivityManager.START_SWITCHES_CANCELED;
            }
        }

        ......

        //Call overloaded method
        return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true /* doResume */, checkedOptions, inTask, outActivity);
    }
>  ActivityStarter.java

        private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                ActivityRecord[] outActivity) {
        int result = START_CANCELED;
        try {
            //Delay layout
            mService.mWindowManager.deferSurfaceLayout();
            //Call startactivityunchecked() method
            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, outActivity);
        } finally {
            final ActivityStack stack = mStartActivity.getStack();
            if (!ActivityManager.isStartResultSuccessful(result) && stack != null) {
                stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
                        null /* intentResultData */, "startActivity", true /* oomAdj */);
            }
            //Restore layout
            mService.mWindowManager.continueSurfaceLayout();
        }

        postStartActivityProcessing(r, result, mTargetStack);

        return result;
    }

Next callstartActivityUnchecked()

>  ActivityStarter.java

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

        //Set the initial state of starting activity, including flag
        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);

        //Calculate mlaunchflags, start flag bit
        computeLaunchingTaskFlags();

        //Calculate msourcestack
        computeSourceStack();

        //Set start flag bit
        mIntent.setFlags(mLaunchFlags);

        //Find reusable activities
        ActivityRecord reusedActivity = getReusableIntentActivity();

       ......

        //Not equal to null indicates that the new activity should be inserted into the existing task stack
        if (reusedActivity != null) {
            if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
                    (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
                Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
            }

            final boolean clearTopAndResetStandardLaunchMode =
                    (mLaunchFlags & (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED))
                            == (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                    && mLaunchMode == LAUNCH_MULTIPLE;

            if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {
                mStartActivity.setTask(reusedActivity.getTask());
            }

            if (reusedActivity.getTask().intent == null) {
                reusedActivity.getTask().setIntent(mStartActivity);
            }

            if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                    || isDocumentLaunchesIntoExisting(mLaunchFlags)
                    || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
                final TaskRecord task = reusedActivity.getTask();

                //Clear task stack
                final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
                        mLaunchFlags);

                if (reusedActivity.getTask() == null) {
                    reusedActivity.setTask(task);
                }

                if (top != null) {
                    if (top.frontOfTask) {
                        top.getTask().setIntent(mStartActivity);
                    }
                    //Trigger onnewintent()
                    deliverNewIntent(top);
                }
            }

        ......

        //Whether to create a new task
        boolean newTask = false;
        final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                ? mSourceRecord.getTask() : null;

        ......

        //Top the activity to be started in the task
        mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
                mOptions);
        if (mDoResume) {
            final ActivityRecord topTaskActivity =
                    mStartActivity.getTask().topRunningActivityLocked();
            if (!mTargetStack.isFocusable()
                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay
                    && mStartActivity != topTaskActivity)) {
                mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                mService.mWindowManager.executeAppTransition();
            } else {
                if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
                    mTargetStack.moveToFront("startActivityUnchecked");
                }
                //Call activitystacksupervisor. Resumefocusedstacktopactivitylocked() method
                mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                        mOptions);
            }
        } else if (mStartActivity != null) {
            mSupervisor.mRecentTasks.add(mStartActivity.getTask());
        }
        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);

        mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
                preferredLaunchDisplayId, mTargetStack);

        return START_SUCCESS;
    }

startActivityUnchecked()The method mainly deals with the start flag, the task stack to be started and so on. The source code of this block is very long, which has been greatly reduced, and only the basic call chain is retained. Interested students can view the source files by themselves. Next, I callActivityStackSupervisorOfresumeFocusedStackTopActivityLocked()Method.

> ActivityStackSupervisor.java

        boolean resumeFocusedStackTopActivityLocked(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {

        if (!readyToResume()) {
            return false;
        }

        //Target stack is mfocused stack
        if (targetStack != null && isFocusedStack(targetStack)) {
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }

        //Get the activityrecord at the top of the mfocusedstack stack
        final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
        if (r == null || !r.isState(RESUMED)) {
            mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
        } else if (r.isState(RESUMED)) {
            // Kick off any lingering app transitions form the MoveTaskToFront operation.
            mFocusedStack.executeAppTransition(targetOptions);
        }

        return false;
    }

Get the activitystack of the activity to be started and call itresumeTopActivityUncheckedLocked()Method.

> ActivityStack.java

boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mStackSupervisor.inResumeTopActivity) {
            //Prevent recursive startup
            return false;
        }

        boolean result = false;
        try {
            mStackSupervisor.inResumeTopActivity = true;
            //Execute resumetopactivityinnerlocked() method)
            result = resumeTopActivityInnerLocked(prev, options);

            final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
            if (next == null || !next.canTurnScreenOn()) {
                checkReadyForSleep();
            }
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }

        return result;
    }
> ActivityStack.java

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    if (!mService.mBooting && !mService.mBooted) {
        //AMS has not been started yet
        return false;
    }

    ......

    if (!hasRunningActivity) {
        //If there is no activity in the current stack, go to the next one. May launch home app
        return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");
    }

    //Next is the target activity. Remove it from the following queues
    mStackSupervisor.mStoppingActivities.remove(next);
    mStackSupervisor.mGoingToSleepActivities.remove(next);
    next.sleeping = false;
    mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(next);

    ......

    //Mresumedactivity refers to the current activity
    if (mResumedActivity != null) {
        //When another activity is in onresume(), pause it first
        pausing |= startPausingLocked(userLeaving, false, next, false);
    }

    ......

    ActivityStack lastStack = mStackSupervisor.getLastStack();
    if (next.app != null && next.app.thread != null) {
        ......
        synchronized(mWindowManager.getWindowManagerLock()) {
            // This activity is now becoming visible.
            if (!next.visible || next.stopped || lastActivityTranslucent) {
                next.setVisibility(true);
            }

            ......

            try {
                final ClientTransaction transaction = ClientTransaction.obtain(next.app.thread,
                        next.appToken);
                // Deliver all pending results.
                ArrayList<ResultInfo> a = next.results;
                if (a != null) {
                    final int N = a.size();
                    if (!next.finishing && N > 0) {
                        if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                                "Delivering results to " + next + ": " + a);
                        transaction.addCallback(ActivityResultItem.obtain(a));
                    }
                }

                if (next.newIntents != null) {
                    transaction.addCallback(NewIntentItem.obtain(next.newIntents,
                            false /* andPause */));
                }

                next.sleeping = false;
                mService.getAppWarningsLocked().onResumeActivity(next);
                mService.showAskCompatModeDialogLocked(next);
                next.app.pendingUiClean = true;
                next.app.forceProcessStateUpTo(mService.mTopProcessState);
                next.clearOptionsLocked();
                transaction.setLifecycleStateRequest(
                        ResumeActivityItem.obtain(next.app.repProcState,
                                mService.isNextTransitionForward()));
                mService.getLifecycleManager().scheduleTransaction(transaction);

            } catch (Exception e) {
                next.setState(lastState, "resumeTopActivityInnerLocked");

                // lastResumedActivity being non-null implies there is a lastStack present.
                if (lastResumedActivity != null) {
                    lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
                }

                Slog.i(TAG, "Restarting because process died: " + next);
                if (!next.hasBeenLaunched) {
                    next.hasBeenLaunched = true;
                } else  if (SHOW_APP_STARTING_PREVIEW && lastStack != null
                        && lastStack.isTopStackOnDisplay()) {
                    next.showStartingWindow(null /* prev */, false /* newTask */,
                            false /* taskSwitch */);
                }
                //Call startspecificactivitylocked()
                mStackSupervisor.startSpecificActivityLocked(next, true, false);
                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                return true;
            }
        }

        // From this point on, if something goes wrong there is no way
        // to recover the activity.
        try {
            next.completeResumeLocked();
        } catch (Exception e) {
            ......
        }
    } else {
        ......
        //Call startspecificactivitylocked()
        mStackSupervisor.startSpecificActivityLocked(next, true, true);
    }

    if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
    return true;
}

Omitted aboveresumeTopActivityInnerLocked()Most of the code in the method has about 400 lines of original code. What should be noted is thatstartPausingLocked()andstartSpecificActivityLocked()Method.

Before you start an activity, if the current activity is in the onresume state, you need to pause it first, that is, call its onpause. This is it.startPausingLocked()Responsibilities of methods. We will not analyze it in detail here, and we will write a separate article to explain the declaration cycle call of activity. In addition, we need to execute the onpause of the current activity before starting the target activity, so we can’t execute the time-consuming task in onpause, which will cause the activity to be stuck when switching.

Another waystartSpecificActivityLocked()That is to start the specified activity. Let’s continue to follow.

> ActivityStackSupervisor.java

   void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        //Find whether the process already exists through AMS
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        //Application process already exists and is bound
        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.longVersionCode,
                            mService.mProcessStats);
                }
                //Call realstartactivitylocked() when the application process already exists
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }

        //Create process if application process does not exist
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

First, use AMS to find out whether the application process already exists. If it already exists and attaches, callrealStartActivityLocked()Start the target activity directly. If the application process does not exist, create the application process first.

In the Android world, who wakes up zygote? The creation process of the application process has been described. Let’s talk about it briefly,ZygoteWhen the process is started, the localsocket server is opened, waiting for the client’s request. AMS sends a request to zygote as the socket client. After zygote receives the request, it forks out the subprocess.

Today I saw a very interesting question,IPC communication in Android is mostly realized through binder mechanism. Why does zygote communicate across processes through socket?To be honest, I don’t know. Welcome to leave your opinion.

Next isrealStartActivityLocked(), as the name suggests, it’s time to start the activity.

> ActivityStackSupervisor.java

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

        if (!allPausedActivitiesComplete()) {
            //The new activity will not be started until all onpause() execution ends
            return false;
        }

        final TaskRecord task = r.getTask();
        final ActivityStack stack = task.getStack();

        beginDeferResume();

        try {
            ......

            //Update process oom adj value
            mService.updateLruProcessLocked(app, true, null);
            mService.updateOomAdjLocked();

            try {

                 ......

                //Add launchactivityitem
                final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
                        r.appToken);
                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                        System.identityHashCode(r), r.info,
                        mergedConfiguration.getGlobalConfiguration(),
                        mergedConfiguration.getOverrideConfiguration(), r.compat,
                        r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                        r.persistentState, results, newIntents, mService.isNextTransitionForward(),
                        profilerInfo));

                //Set lifecycle state
                final ActivityLifecycleItem lifecycleItem;
                if (andResume) {
                    lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
                } else {
                    lifecycleItem = PauseActivityItem.obtain();
                }
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

                // key
                //// call clientlecyclemanager. Scheduletransaction()
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);

                   ......

            } catch (RemoteException e) {
                if (r.launchFailed) {
                    //Second start failed, finish activity
                    mService.appDiedLocked(app);
                    stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
                            "2nd-crash", false);
                    return false;
                }
                //First failure, restart process and try again
                r.launchFailed = true;
                app.activities.remove(r);
                throw e;
            }
        } finally {
            endDeferResume();
        }

        r.launchFailed = false;
           ......
        return true;
    }

The key point above is this code,mService.getLifecycleManager().scheduleTransaction(clientTransaction);

It’s here againClientTransaction。 Remember the pause activity mentioned above, which is also implemented through this class. I was going to write a separate article on the life cycle for further analysis. It seems that I can’t escape. Here’s a little bit of clienttransaction.

FirstmService.getLifecycleManager()The return isClientLifecycleManagerObject, which is a new class in Android 9.0. Let’s take a look at itscheduleTransaction()Method.

> ClientLifecycleManager.java

void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
    final IApplicationThread client = transaction.getClient(); // -> ApplicationThread
    transaction.schedule(); // ClientTransaction
    if (!(client instanceof Binder)) {
        transaction.recycle();
    }
}

Follow-upschedule()Method.

> ClientTransaction.java

public void schedule() throws RemoteException {
    mClient.scheduleTransaction(this);
}

TheremClientIs the iaapplicationthread type, which isApplicationThreadThe binder proxy object of, so the cross process call toApplicationThread.scheduleTransaction()Method.ApplicationThreadyesActivityThreadNo matter whether it is applicationthread or activitythread, there is no scheduletransaction() method, so the parent class is calledClientTransactionHandlerMethod.

> ClientTransactionHandler.java

public abstract class ClientTransactionHandler {

    /** Prepare and schedule transaction for execution. */
    void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        //The sendmessage() method is implemented in the activitythread class
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }

  }

Take a look back in the activitythread classsendMessage()Method.

> ActivityThread.java

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}

Here tomHSentEXECUTE_TRANSACTIONMessage, and carry the transaction. MH is a nameHHandler class of. It is responsible for the main thread message processing and defines about 50 kinds of events. Find out how it handles the execute? Transaction message.

> ActivityThread.java

case EXECUTE_TRANSACTION:
            final ClientTransaction transaction = (ClientTransaction) msg.obj;
           //Execute transactionexecutor. Execute()
           mTransactionExecutor.execute(transaction);
           if (isSystem()) {
                transaction.recycle();
           }

CalledTransactionExecutorOfexecute()Method.

> TransactionExecutor.java`

public void execute(ClientTransaction transaction) {
    final IBinder token = transaction.getActivityToken();
    log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

    //Execute callback
    executeCallbacks(transaction);

    //Execution lifecycle state
    executeLifecycleState(transaction);
    mPendingActions.clear();
    log("End resolving transaction");
}

First look at it.executeCallbacks()Method.

> TransactionExecutor.java

@VisibleForTesting
public void executeCallbacks(ClientTransaction transaction) {

    ......

    final int size = callbacks.size();
    for (int i = 0; i < size; ++i) {
        final ClientTransactionItem item = callbacks.get(i);

        ......

        item.execute(mTransactionHandler, token, mPendingActions);
        item.postExecute(mTransactionHandler, token, mPendingActions);

        ....
}

That’s the core code. To execute theexecute()Method andpostExecute()Method. Remember beforerealStartActivityLocked()Call in methodaddCallback()Are the parameters passed in?

clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), ......);

That is to say, it will executeLaunchActivityItemOfexecute()Method.

> LaunchActivityItem.java

@Override
public void execute(ClientTransactionHandler client, IBinder token,
        PendingTransactionActions pendingActions) {
    Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
    ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
            mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
            mPendingResults, mPendingNewIntents, mIsForward,
            mProfilerInfo, client);
    //Call activitythread. Handlelaunchactivity()
    client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

Go around, go back to activitythread again, and execute ithandleLaunchActivity()Method.

> ActivityThread.java

@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, Intent customIntent) {

    ......

    final Activity a = performLaunchActivity(r, customIntent);

    ......

    return a;
}
> ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
     ActivityInfo aInfo = r.activityInfo;
     if (r.packageInfo == null) {
         r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                 Context.CONTEXT_INCLUDE_CODE);
     }

     //Get componentname
     ComponentName component = r.intent.getComponent();
     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);
     }

     //Get context
     ContextImpl appContext = createBaseContextForActivity(r);
     Activity activity = null;
     try {
         java.lang.ClassLoader cl = appContext.getClassLoader();
         //Reflection create activity
         activity = mInstrumentation.newActivity(
                 cl, component.getClassName(), r.intent);
         StrictMode.incrementExpectedActivityCount(activity.getClass());
         r.intent.setExtrasClassLoader(cl);
         r.intent.prepareToEnterProcess();
         if (r.state != null) {
             r.state.setClassLoader(cl);
         }
     } catch (Exception e) {
          ......
     }

     try {
         //Get application
         Application app = r.packageInfo.makeApplication(false, mInstrumentation);

         if (activity != null) {
             CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
             Configuration config = new Configuration(mCompatConfiguration);
             if (r.overrideConfig != null) {
                 config.updateFrom(r.overrideConfig);
             }
             Window window = null;
             if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                 window = r.mPendingRemoveWindow;
                 r.mPendingRemoveWindow = null;
                 r.mPendingRemoveWindowManager = null;
             }
             appContext.setOuterContext(activity);
             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);

             if (customIntent != null) {
                 activity.mIntent = customIntent;
             }
             r.lastNonConfigurationInstances = null;
             checkAndBlockForNetworkAccess();
             activity.mStartedActivity = false;
             int theme = r.activityInfo.getThemeResource();
             if (theme != 0) {
                 //Set theme
                 activity.setTheme(theme);
             }

             activity.mCalled = false;
             //Execute oncreate()
             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.setState(ON_CREATE);

         mActivities.put(r.token, r);

     } catch (SuperNotCalledException e) {
         throw e;

     } catch (Exception e) {
        ......
     }

     return activity;
 }

It’s here againInstrumentationThe shadow of, respectively callednewActivity()Method andcallActivityOnCreate()Method.

newActivity()Method reflection creates an activity and calls itattach()Method.

> Instrumentation.java

public Activity newActivity(Class<?> clazz, Context context,
        IBinder token, Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        Object lastNonConfigurationInstance) throws InstantiationException,
        IllegalAccessException {
    Activity activity = (Activity)clazz.newInstance();
    ActivityThread aThread = null;
    // Activity.attach expects a non-null Application Object.
    if (application == null) {
        application = new Application();
    }
    activity.attach(context, aThread, this, token, 0 /* ident */, application, intent,
            info, title, parent, id,
            (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
            new Configuration(), null /* referrer */, null /* voiceInteractor */,
            null /* window */, null /* activityConfigCallback */);
    return activity;
}

callActivityOnCreate()Method callActivity.performCreate()Method, final callbackonCreate()Method.

> Instrumentation.java

public void callActivityOnCreate(Activity activity, Bundle icicle) {
    prePerformCreate(activity);
    activity.performCreate(icicle);
    postPerformCreate(activity);
}
> Activity.java

final void performCreate(Bundle icicle) {
    performCreate(icicle, null);
}

final void performCreate(Bundle icicle, PersistableBundle persistentState) {
    mCanEnterPictureInPicture = true;
    restoreHasCurrentPermissionRequest(icicle);
    //Callback oncreate()
    if (persistentState != null) {
        onCreate(icicle, persistentState);
    } else {
        onCreate(icicle);
    }
    writeEventLog(LOG_AM_ON_CREATE_CALLED, "performCreate");
    mActivityTransitionState.readState(icicle);

    mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
            com.android.internal.R.styleable.Window_windowNoDisplay, false);
    mFragments.dispatchActivityCreated();
    mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
}

See here, have a feeling of relief, finally implementedonCreate()Method. In fact, each life cycle callback of activity is a similar call chain.

Do you remember which method traced all the way to oncreate? yesTransactionExecutorOfexecute()Method.

> TransactionExecutor.java`

public void execute(ClientTransaction transaction) {
    final IBinder token = transaction.getActivityToken();
    log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

    //Execute callback
    executeCallbacks(transaction);

    //Execution lifecycle state
    executeLifecycleState(transaction);
    mPendingActions.clear();
    log("End resolving transaction");
}

Preceding analysisexecuteCallBack()Trace all the way to oncreate(), and then analyzeexecuteLifecycleState()Method.

> TransactionExecutor.java

private void executeLifecycleState(ClientTransaction transaction) {
     final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
     if (lifecycleItem == null) {
         // No lifecycle request, return early.
         return;
     }

     final IBinder token = transaction.getActivityToken();
     final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);

     if (r == null) {
         // Ignore requests for non-existent client records for now.
         return;
     }

     // Cycle to the state right before the final requested state.
     cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);

     // Execute the final transition with proper parameters.
     lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
     lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
 }

Very familiar, see againlifecycleItem.execute()。 TherelifecycleItemStill inrealStartActivityLocked()Method.

  lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());

But in the analysisResumeActivityItemBefore, pay attentionexecute()Before methodcycleToPath()Method. The specific source code will not be analyzed. Its function is to synchronize with the upcoming lifecycle state according to the last executed lifecycle state. It’s not so easy to understand. For example, the oncreate() method was called back last time. This time, what to do isResumeActivityItem, and one in the middleonStart()State, thencycleToPath()Method will call backonStart(), which is calledActivityThread.handleStartActivity()。 andhandleLaunchActivity()Similar call chain.

So, back toResumeActivityItem.execute()

> ResumeActivityItem.java

@Override
public void execute(ClientTransactionHandler client, IBinder token,
        PendingTransactionActions pendingActions) {
    Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
    client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
            "RESUME_ACTIVITY");
    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

Still callActivityThread.handleResumeActivity()。 But there is something special here. I have to carry it out.

The first WeChat public address is:Theory of mindFocus on the original knowledge sharing of Java and Android, and solve the problem of leetcode.

More new original articles, scan and follow me!

Start the activity process

> ActivityThread.java

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
         String reason) {
        ......
    
         r.activity.mVisibleFromServer = true;
         mNumVisibleActivities++;
         if (r.activity.mVisibleFromClient) {
            //Page visible
             r.activity.makeVisible();
         }
     }

     //Idler will be executed when the main thread is idle
     Looper.myQueue().addIdleHandler(new Idler());
 }

makeVisible()Method to make decorview visible.

> Activity.java

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}

The last thing to notice isLooper.myQueue().addIdleHandler(new Idler())。 Because of the length, I won’t introduce it here. I will analyze it later when I write the activity life cycle separately. You can first go to the source code to find the answer.

summary

All the way through the analysis, the activity finally shows to the user.

In fact, the article is smelly and long. Many people may have questions. Is it really useful to read these articles? In my opinion, the two most important things for a programmer are basic skills and internal skills. Good basic skills can make us easily master a technology, and deep internal skills can make us solve problems. Source code can bring you, it is these.

Recently I read the source code of some components in jetpack. The next article should be about jetpack. Coming soon!

The first WeChat public address is:Theory of mindFocus on the original knowledge sharing of Java and Android, and solve the problem of leetcode.

More new original articles, scan and follow me!

Start the activity process