preface
Learning Android online recently, I want to study the realization principle of Android application. Let’s start with the most intuitive Activity flow
Activity Lifecycle
- An activity has four states from start to finish.
- The activity life cycle
- The process priority of the activity
1. The four states of the activity
The activity has four states and its life cycleThere are four states of an activity:running-paused-stopped-killed
- Running -> The activity currently displayed on the screen (at the top of the task stack), the user can see the status.
- Poused -> is still visible to the user, but the interface focus has been lost, and the Activity cannot interact with the user.
- Stopped -> The user cannot see the current interface and cannot interact with the user completely overwritten.
- Killed -> The current interface was destroyed, waiting for the system to be recycled
From the picture above, we know that:
OnCreate ()-> onStart ()->onResume() Is currently called the active state (Running). The activity is at the top of the task stack and can interact with the user.
Running — >Paused The onPause () in the life cycle of the executed Activity is currently called Paused, and the Activity has lost focus but is still visible (including partially visible).
Paused — >Running :OnResume() currently returns to the Running state, which occurs when the user presses the home button and returns to the current activity screen.
Paused — >Stoped :onStop() The Activity has been completely covered by another Activity, and the Activity has become invisible, so the system will often force the Activity to end due to lack of memory. Paused — >Stoped
Stoped — > KILLED :onDestroy() The Activity was destroyed by the system. An Activity can die anywhere when it is paused or stopped, because the system can forcibly end the Activity due to running out of memory.
2. Activity lifecycle
OnCreate (): When we click on the activity, the system calls the activity’s onCreate() method, which initializes the setContentLayout () method of the current layout.
OnStart (): After the onCreate() method completes, the activity enters the onStart() method. The current activity is visible to the user, but has no focus and cannot interact with the user.
OnResume (): After the onStart() method is complete, the activity enters the onResume() method. The current state of the activity is Running and it can interact with the user.
OnPause () When another activity overwrites the current acitivty, the current activity enters the onPause() method. The current activity is visible but cannot interact with the user.
After the onStop() onPause() method is complete, the activity enters the onStop() method. The activity is not visible to the user and may be reclaimed by the system if the system memory is insufficient. Therefore, the current method can generally do resource reclamation.
After the onDestory() onStop() method completes, the activity enters the onDestory() method to end the current activity.
The onRestart() onRestart() method is called when the user enters the current activity again after pressing home(). The call order onPause () – > onStop () – > onRestart () – > onStart () – > onResume ().
3. The activity priority
Foreground Process > Visible Process > Service Process > Background Process > Empty Process
Foreground process is a Foreground process that is necessary for what the user is currently doing. A Foreground process is considered Foreground if it meets any of the following conditions:
1. The current process activity is interacting with the user. 2. The process holds a Service that is in one of the following states :① The Service is bound to the Activity that the user is interacting with. StartForeground (); Service foreground (); ③ The Service is executing its life cycle callbacks (onCreate(), onStart(), or onDestroy())). 3. The process holds a BroadcostReceiver that is executing the onReceive() method
A Visible process is a process that does not contain any foreground components but is Visible to the user on the screen. A process is considered visible when any of the following conditions are met:
- The process holds an activity that is no longer foreground and is in onPouse() state. The current overridden activity exists as a dialog.
- A process has a service that is bound to a visible Activity.
Service processes are not tied to what the user can see, but they generally do things that the user cares about, such as playing music in the background, downloading data in the background, and so on. So the system will try to keep them running unless the system memory is insufficient to support foreground and visible processes
1. A process is a service if it is running a service that is started by startService() and does not have either of the higher priorities
If a Background process does not fall into the above three categories, but has an activity that is not visible to the user, the Background process does not directly affect the user experience. The system will kill Background processes arbitrarily for foreground, visible, and server processes
The activity’s onStop() is called, but onDestroy() has no called state. This process is a background process.
Empty process A process is considered empty if it does not contain any active application components.
This process has no running data and is not killed by the system. It is an empty process and can be killed easily.
Activity Startup process
Before you start parsing an Activity, take a look at some of the diagrams on the Web that describe the Activity process to get a sense of it
Look at the diagram from the code to analyze
Analyze the Activity startup process
An Activity is one of the four components of an Android application. It is responsible for managing the user interface of an Android application. Typically, an application contains many activities, and they may be executed in a process. It may also be executed in a different process. We mainly analyze the startup process of Activity and the management logic of AMS to Activity by starting activities in different processes. There are two applications App1 and App2. Start Activity B in App2 by clicking button on Activity A in App1. Analyze the ActivityB startup process above to understand the logic of AMS managing activities.
Step 1: Activity A tells the AMS service to prepare to start Activity B
Step 2: THE AMS service processes and notifies the process of Activity A to pause Activity A, and when the process of Activity A is finished processing. Notifies AMS service Activity A that pause work has finished.
Step 3: The process in which Activity B is running is not started, and the AMS service starts a new process first. After the new process is started. Notifies AMS that the service process has started.
Step 4: THE AMS service notifies a new process to start Activity B. After the new process starts Activity B. Notifies AMS service Activity B that it has started.
Before analyzing the Activity startup process, we first introduce the application process and AMS service communication method. AMS services and application processes belong to different processes. The communication must have something to do with Binder. We know that the services of the system are all servers that implement Binder. In order for an application process to communicate with it, it must obtain its proxy.
But how does the AMS service communicate with the application process? After a new application process is created, the system first starts an ActivityThread, which is the main thread of the application process. An ApplicationThread object is created when an ActivityThread is created. This ApplicationThread implements a Binder server. Notify the AMS service of the creation of a new process and send the proxy of your process’s ApplicationThread to the AMS service at the same time. A proxy object in the AMS service that holds the ApplicationThread for all application processes.
So in order for AMS to send messages to the application process, all it needs to do is get the proxy object of the target ApplicationThread.
Because the Activity startup process is more complex, step by step analysis, so it is easier to understand.
Step 1: Get ready to boot
1. Activity.startActivity
Click button in Activity A to start the activity. B calls the startActivity method of the activity.
public void startActivity(Intent intent, @Nullable Bundle options) { if (options ! = null) { startActivityForResult(intent, -1, options); … } In this method, the activity’s startActivityForResult method is called to start the activity B described by the Intent, with the second parameter -1 indicating that no result is required
2. Activity. startActivityForResult
public void startActivityForResult( String who, Intent intent, int requestCode, @Nullable Bundle options) {
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, who, intent, requestCode, options); ... }Copy the code
In this method, the execStartActivity method of the Instrumentation is finally called to start the Activity. Interactions between applications and systems are left to Instrumentation to facilitate monitoring of these activities.
. Here we look at the parameters mMainThread getApplicationThread (), the main thread ActivityThread mMainThread is application app1 process, GetApplicationThread returns the inner class ApplicationThread of an ActivityThread, which is a Binder object. A server side implemented with Binder is passed into the AMS service with parameters. The AMS service stores its clients. This allows AMS to communicate with application processes through it.
The mToken parameter is A Binder proxy object that points to A saved ActivityRecord in AMS. The mToken represents Activity A, which is passed to AMS as A parameter. AMS uses this to get information about Activity A.
3. Instrumentation. execStartActivity
public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread;
Try {/ / transmission between the intent to do the process of preparation intent. MigrateExtraStreamToClipData (); intent.prepareToLeaveProcess(); // Interprocess transfer, Finally call to AMS service int result = ActivityManagerNative. GetDefault () startActivity (whoThread, who getBasePackageName (), the intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! = null ? target.mEmbeddedID : null, requestCode, 0, null, options); checkStartActivityResult(result, intent); }... }Copy the code
In this method, the AMS proxy object is obtained by getDefault method of ActivityManagerNative, and then its startActivity method is called to the AMS service startActivity method through inter-process transmission. Inter-process communication is not detailed here.
Up to this point, all of the above has been run in the process of application App1.
The following code enters the AMS service of the SystemServer process
4. AMS.startActivity
public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle options) { return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, options, UserHandle.getCallingUserId()); } The IBinder is the binder that points to Activity A’s ActivityRecord, and its caller is the ApplicationThread of App1. This method then calls the startActivityAsUser method
5. AMS. startActivityAsUser
public final int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {… return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, null, null, options, false, userId, null, null); } calls the startActivityMayWait method of activityStackSupervisor. ActivityStackSupervisor is the core class of Activity scheduling. All activities related to Activity scheduling are handled in ActivityStackSupervisor, which mainly manages tasks and stacks. It was created when AMS was launched.
6. startActivityMayWait
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, WaitResult outResult, Configuration config, Bundle options, boolean ignoreTargetSecurity, int userId, IActivityContainer iContainer, TaskRecord inTask) {… Intent = new intent (intent); // The PMS service queries the information about the Activity B to start according to the intent and stores it in ActivityInfo. ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId); …
// Determine the current active stackCopy the code
ActivityContainer container = (ActivityContainer)iContainer; final ActivityStack stack; if (container == null || container.mStack.isOnHomeDisplay()) { stack = mFocusedStack; } else { stack = container.mStack; }
... Int res = startActivityLocked(Caller, intent, resolvedType, aInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity, componentSpecified, null, container, inTask); ... }Copy the code
In the resolveActivity method, the PMS service queries the incoming ActivityB based on the Intent.
Then look for the appropriate ActivityStack. In which ActivityStack the Activity to be launched should be placed. The iContainer passed in from the parameter is null, so the Activity to be launched should be managed in mFocusStack.
MFocusStack refers to the ActivityStack that is currently in focus.
Android typically has the following activitystacks
· Home Stack, which is the Stack of the Launcher. In fact, other system interfaces also run on the Stack, such as recent tasks. SystemUI etc.
FullScreen, Stack. The Stack where the full-screen Activity is located. But actually in split screen mode. Stack with Id 1 takes up only half the screen.
· The Freeform Activity is located in the Stack
As we’ll see later, in split screen mode. Half of the screen executes a fixed application. So this is the Docked Stack right here
· Pinned Stack is the Stack where the Activity is Pinned
The first and second are the more commonly used stacks, while the latter three are mainly related to the multi-window mode. As you can see, most applications are actually in the same Stack, the FullScreenStack.
The startActivityLocked method is then called to resume the Activity.
7. startActivityLocked
final int startActivityLocked(IApplicationThread caller, Intent intent, String resolvedType, ActivityInfo aInfo, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int realCallingPid, int realCallingUid, int startFlags, Bundle options, boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container, TaskRecord inTask) {
//callerApp represents the application process of the caller. App1 ProcessRecord callerApp = null; if (caller ! = null) {/ / based on the caller find AMS save App1 processRecord object callerApp = mService. GetRecordForAppLocked (caller); if (callerApp ! CallingPid = callerApp.pid; callingUid = callerApp.info.uid; }... }... // That is, the caller's Activity component ActivityRecord sourceRecord = null; // Return the result of the Activity component ActivityRecord resultRecord = null; if (resultTo ! ResultTo Binder object (ActivityRecord) {resultTo Binder object (ActivityRecord) {resultTo Binder object (ActivityRecord) isInAnyStackLocked(resultTo); If (sourceRecord! = null) { if (requestCode >= 0 && ! sourceRecord.finishing) { resultRecord = sourceRecord; } } } final int launchFlags = intent.getFlags(); ... // Based on the prepared information, create an ActivityRecord object to be launched. ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage, Intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, requestCode, componentSpecified, voiceSession ! = null, this, container, options); ... // Take the ActivityRecord of the newly created target Activity as an argument, Continue to call startActivityUncheckedLocked method to start the err = startActivityUncheckedLocked (r, sourceRecord voiceSession, voiceInteractor, startFlags, true, options, inTask); ... return err; }Copy the code
The StartActivityLocked method is used to create an ActivityRecord object for the Activity to be started.
Binder objects that represent Activity A are passed in as parameters to get ActivityRecord information for Activity A. It then gets the PID of the calling process and the UID of the calling program.
Create an ActivityRecord object r based on this information and ainFO. On behalf of the ActivityB.
This gets the component information for the caller’s Activity A and the target Activity B that is about to start.
Stored in sourceRecord and R, respectively
The last call startActivityUncheckedLocked method to start the Activity B
8. startActivityUncheckedLocked
final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, Bundle options, TaskRecord inTask) {
// Obtain the corresponding boot mode according to flag. There is no startup mode set in our code, so the default should be standard mode. All three variables should be false Final Boolean launchSingleTop = R.launchMode == ActivityInfo.launch_singLE_TOP; final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE; final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK; ... // The task ActivityStack targetStack that is about to start the activity; ... // create a newTask Boolean newTask = false; ... // Get Activity A's Task and assign it to B, i.e. both activities are in the same Task else if (sourceRecord! = null) {Copy the code
Task Final TaskRecord sourceTask = sourcerecord.task; TargetStack = sourcetask.stack; targetStack.moveToFront(“sourceStackToFront”); ActivityStack is the same as the current Task. If not, move the current Task to the top of ActivityStack final TaskRecord topTask = targetStack.topTask(); if (topTask ! = sourceTask) { targetStack.moveTaskToFrontLocked(sourceTask, noAnimation, options, r.appTimeTracker, “sourceTaskToFront”); }… / / the current Activity Stack mLastPausedActivity. Set to null targetStack mLastPausedActivity = null; / / call startActivityLocked method targetStack. StartActivityLocked (r, newTask doResume, keepCurTransition, options);
The main work handled in this method is the processing of the four launch modes of the Activity. The different launch modes are determined by the flags in the Intent, and the different launch modes are determined by the position of the Activity in the Task.
This method is complicated. We only looked at the code that was relevant to our logic.
The processing of startup mode is not analyzed in detail.
Since we have not set the startup mode for Activity B, Activity B defaults to the standard startup mode. Activity A and Activity B should be in the same Task.
As we have already analyzed, it is common for all applications’ activities to reside in the same ActivityStack. The ActivityStack on which ActivityA is located acts as the ActivityStack that starts Activity B.
Let’s look at the relationship between Task and ActivityStack using a diagram.
The relationship between Task and Stack
Each Activity in the Android system is located in a Task. A Task can contain multiple activities, and there can be multiple instances of the same Activity. In Androidmanifest.xml, we can use Android :launchMode to control the Activity instance in a Task.
Also, in startActivity. We can also use setFlag to control the instance of an Activity launched in a Task.
The significance of Task management also lies in the recent Task list and Back stack. When you use the multi-task button (on some devices, long press the Home button). Some devices are specially provided with a multi-task key) when calling up a multi-task. You actually get a list of recently started tasks from ActivityManagerService.
In fact, within ActivityManagerService and WindowManagerService internal management, there is another layer of containers outside the Task. This container may not be felt or used by either the developer or the user. But it is very important, that is Stack, the Android system of multi-form management, is built on the Stack data structure. A Stack contains multiple tasks. Multiple activities (Windows) in a Task
Finally, startActivityLocked of the obtained ActivityTask is called to continue the startup
9. ActivityStack . startActivityLocked
final void startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume, boolean keepCurTransition, TaskRecord rTask = r.task; final int taskId = rTask.taskId;
TaskRecord task = null; if (! NewTask) {// Iterate through all AMS tasks, find the target Task, and add the Activity to the top of the Task stack. for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { task = mTaskHistory.get(taskNdx); if (task.getTopActivity() == null) { // All activities in task are finishing. continue; } if (task == r.task) { if (! startIt) { task.addActivityToTop(r); r.putInHistory(); ... // Pass the resume bit true. Then call resumeTopActivitiesLocked methods continue to launch the Activity the if (doResume) {mStackSupervisor. ResumeTopActivitiesLocked (this, r, options); }Copy the code
This method variables all tasks, finds the target Task, and adds the ActivityRecord of the Activity to the top of the stack
10. ActivityStackSuperVisor. resumeTopActivitiesLocked
boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target, Bundle targetOptions) {
Call resumeTopActivityLocked if (isFrontStack(targetStack)) {result = targetStack.resumeTopActivityLocked(target, targetOptions); } // Then go through and find the frontStack task running resumeTopActivityLocked for (int displayNdx = mActivityDisplays. Size () -1; displayNdx >= 0; - displayNdx) {... } if (isFrontStack(stack)) { stack.resumeTopActivityLocked(null); }...Copy the code
The main function of this method is to find frontStack and call its resumeTopActivityLocked methods.
Because our ActivityA starts ActivityB in the same Task, the current Task is frontStack, so call ActicityStack resumeTopActivityLocked directly.
ResumeTopActivityLocked method have call resumeTopActivityInnerLocked method directly
- ActivityStack. resumeTopActivityInnerLocked
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
... // Find the first ActicityRecord final ActivityRecord at the top of the stack that is not in the finishing state = topRunningActivityLocked(null); ... // Call startPausingLocked to suspend the previous Activity if (mResumedActivity! = null) { if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Pausing " + mResumedActivity); pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause); }Copy the code
Just look at the key parts of this approach.
The topRunningActivityLocked method is first called to find the ActicityRecord that is not currently at the top of the stack in fininshing state.
We just added the ActicityRecord R to the top of the current Task stack, so this next is the ActicityRecord that will start On AcElasticity B
The startPausingLocked method is called to suspend the previous Activity when mResumedActivity is not Null. ActivityStack has three member variables. MResumedActivity represents the active Activity in the stack. The acelasticity active on the stack is acelasticity A. MLastPausedActivity represents the Activity that was last paused. MLastPausingActivity is the Activity that is being paused in the current stack.
MResumedActivity indicates that Activity A is not Null, so startPasingLocked is called to suspend Activity A.
This concludes the first part of the Activity launch process. Summarize the main work done in this part
-
Call the Activity’s startActivity method to start the target Activity
-
Call the Instrumentation method execStartActivity method. Easy Instrumentation for interaction monitoring
-
The above part is running in the process of App1. The startActivity method that calls AMS is then invoked in the AMS service via interprocess communication. The SystemServer process is displayed.
-
This is then processed by startActivityMayWait, a method in the AMS class ActivityStackSupervisor that manages the core scheduling on Association to Advantage. In this method, the target Activity information is queried from the PMS based on the Intent
-
The startActivityLocked method of ActivityStackSuperVisor basically looks for the processRecord information in AMS that is calling the process, and the ActivityRecord information that is calling the Activity. The target Activity has not been started, so you need to create an ActivityRecord for the target Activity.
-
ActivityStackSuperVisor StartActivityUncheckedLocked method is mainly to deal with the logic of startup mode, according to the different startup mode, find the corresponding to ActivityStack, Then the corresponding ActivityStack is processed
-
ActivityStack adds the target Activity to the top of the corresponding Task stack
-
Call the resumeTopActivityLocked method of ActivityStackSuperVisor to find the Task in the foreground. Then call its resumeTopActivityLocked method to activate the target Activity.
-
The current Task stack starts Pasuing the Activity called
The above steps complete the collection and processing of AMS’s information on the calling Activity and the target Activity. According to the startup mode, the target Activity method will be added to the stack. Then the target stack starts to Pause the currently active Activity to make room for the target Activity.
Moving on to part 2, how to call an Activity from Resume to Pause
Step 2: Pause Activity A
12. ActivityStack.startPausingLocked
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming, Boolean dontWait) {//mResumeActivity represents the currently active Activity. Activity A ActivityRecord prev = mResumedActivity; … MResumedActivity = null; mResumedActivity = null; // The Activity to be in the pasuing state is Activity A mPausingActivity = prev; mLastPausedActivity = prev; … // Set the state of Activity A to PAUSING prev.state = activitystate.pausing; / / find the current topActivity is on the top of the stack Activity B final ActivityRecord next = mStackSupervisor. TopRunningActivityLocked (); // The calling process is not null, and the calling process ApplicationThread is not null. = null && prev.app.thread ! = null) {try {…… By the calling process ApplicationThread notice calling process schedulePauseActivity method prev. App. Thread. SchedulePauseActivity (prev. AppToken, prev.finishing, userLeaving, prev.configChangeFlags, dontWait);
Get the prev of the Activity to be paused, which is mResumeActivity, which represents the currently active Activity A, and set mResumeActivity to null, since Activity A will be paused immediately. Activity B has not been started yet. So there is no active Activity.
Set mPausingActivity to Activity B, which represents an Activity in the Pausing state. State = activityState. PAUSING, set the state of Activity A to PAUSING.
Prev is an ActivityRecord object for Activity A. Prev. App represents the ProcessRecord of the Activity A process. Prev. App is the ApplicationThread of the Activity A process. ApplicationThread is a Binder object that serves as a Binderclient for each process in the AMS service. This allows AMS to send messages to the application process.
The interprocess communication Binder mechanism is not explained here
This method finally calls the schedulePauseActivity method of Activity A’s process through interprocess communication to continue the Activity Pause process.
13. ApplicationThread. schedulePauseActivity
public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, boolean dontReport) { sendMessage( finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY, token, (userLeaving ? 1 : 0) | (dontReport ? 2 : 0), configChanges); }
The application process’s ApplicationThread is an internal class of ActivityThread. The parameter token is A Binder object that points to an ActivityRecord in AMS that corresponds to Activity A. The finished bit is false to enter the pause state.
This method sends a PAUSE_ACTIVITY Message to Handler, and then looks at Handler’s handleMessage method
14. H.handleMessage
ase PAUSE_ACTIVITY:
... handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) ! = 0, msg.arg2, (msg.arg1&2) ! = 0);Copy the code
Call the handlePauseActivity method to handle it
15. ActivityThread.handPauseActivity
private void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, boolean dontReport) { ActivityClientRecord r = mActivities.get(token); if (r ! = null) {
// Call Activity A's onPause method performPauseActivity(Token, finished, R.isrehoneycomb ()); // Wait for pause data to finish writing. if (r.isrehoneycomb ()) {queuedwork.waittofinish (); }... Try {// Interprocess communication. Call the AMS ActivityPause method ActivityManagerNative. GetDefault (.) activityPaused (token); } catch (RemoteException ex) {Copy the code
The mActivities set in ActivityThread stores all the activities in the current process. Each Activity is represented by an ActivityClientRecord, which is stored with binder keys. All activities in the same AMS service are also stored in a collection. Each Activity in AMS service is represented by ActivityRecord. The same key value is also a Binder object. Binder objects are known to be transferable between processes, so Binder is used for the key value. The ability to match the ActivityClientRecord of an application process to the ActivityRecord in AMS.
The binder object passed in AMS is used to retrieve the ActivityClientRecord object in the application process. Because the parameter token points to the ActivityRecord in AMS that represents Activity A, we get Activity A’s ActivityClientRecord here.
It then calls the preformPauseActivity method, which calls Activity A’s onSaveInstance method. Then call Activity A’s onPause method.
Because the onSaveInstance method stores the current Activity information and writes it to disk for I/O operations, queuedWork.waittoFinish () is used to wait for the storage operation to complete.
Finally, there is interprocess communication again, calling AMS’s activityPaused method, which leaves Activity A’s process. Enter the AMS SystemServer process.
16. AMS.activityPaused
Public final void activityPaused(IBinder token) {// Obtain ActivityStack ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack ! = null) {/ / call the target ActivityStack activityPauseLocked method stack. ActivityPausedLocked (token, false); }}
The Token represents Activity A, which uses the Token to locate the ActivityStack of Activity A, and the target Stack handles the activityPause logic
17. ActivityStack.activityPauseLocked
final void activityPausedLocked(IBinder token, Boolean timeout) {final ActivityRecord = isInStackLocked(token); if (r ! Mhandler. removeMessages(PAUSE_TIMEOUT_MSG, r); If (mPausingActivity == r) {// Call the completePauseLocked method to continue running the pause logic completePauseLocked(true); }…
The Token represents the binder object of the Activity. The Token is used to obtain the ActivityRecord of the Activity A, remove the Pause timeout message, When the ActivityRecord running pause logic is the same Activity as the ActivityRecord running pause logic. You can call the completePauseLocked method to finish the last logic of the Activity A Pause.
18. ActivityStack. completePauseLocked
if (resumeNext) { final ActivityStack topStack = mStackSupervisor.getFocusedStack(); if (! mService.isSleepingOrShuttingDown()) { mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null); }… }
A resumeNext is the argument passed in by completePauseLocked, which is true. Call mStackSupervisor getFocusedStack () method to get being processed ActivityStack. MService. IsSleepingOrShuttingDown. Concluded that the normal activation of the AMS service, and then call mStackSupervisor resumeTopActivitiesLocked continue processing.
19. ActivityStackSuperVisor. resumeTopActivitiesLocked
This step was dealt with in Step 10, which is the same logic as step 10, assuming that the current ActivityStack is frontStack. Call ActivityStack’s resumeTopActivityLocked directly.
ActivityStack resumeTopActivityLocked method directly to have invoked the ActivityStack resumeTopActivityInnerLocked method.
20. ActivityStack. resumeTopActivityInnerLocked
Private Boolean resumeTopActivityInnerLocked (ActivityRecord prev, Bundle options) {… B Final ActivityRecord Next = topRunningActivityLocked(NULL); …
// We know that when the Activity A changes to the pause state. We just set mResumeActivity to Null if (mResumedActivity! = null) { if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Pausing " + mResumedActivity); pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause); }... // The Activity to be started is in a new process and has not been started yet. = null && next.app.thread ! = null) {... } else {... / / to start the target Activity mStackSupervisor. StartSpecificActivityLocked (next, true, true); }Copy the code
You’ve already gone through the logic of this method in step 11, when the method is called to activate the target Activity. If mResumeActivity is not empty, it indicates that there is an active Activity in the active state. You must change the original Activity to pause state first.
When Activity A changes to the Pause state. If you go to this method again to activate the target Activity, the mResumeActivity is already empty, so you do not need to run pause logic.
The app and ApplicationThread of the target Activity NEXT are empty. The process in which Activity A is running has not yet been created. Run directly mStackSupervisor startSpecificActivityLocked to start a new Activity.
Step 3: Start the new process
21. ActivityStackSuperVisor. startSpecificActivityLocked
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, Boolean checkConfig) {/ / obtain the target Activity process ProcessRecord ProcessRecord app. = mService getProcessRecordLocked (r.p rocessName, r.info.applicationInfo.uid, true); … // Empty if (app!) because the process of the target Activity has not been created. = null && app.thread ! = null) {…
}
//
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
Copy the code
This method is simple and looks for the appropriate ProcessRecord based on the target Activity’s ProcessName. Infer from the process APP and ApplicationThread in processRecord. Assuming that these two variables are not empty, the process and execution environment for the target Activity are already available. We know that the target Activity process has not been started yet, so we need to call AMS to start a target Activity process first.
22. AMS. startProcessLocked
Final ProcessRecord startProcessLocked(String processName, ApplicationInfo Info…) { long startTime = SystemClock.elapsedRealtime(); ProcessRecord app; if (! isolated) { app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
String hostingNameStr = hostingName ! = null ?Copy the code
hostingName.flattenToShortString() : null;
... startProcessLocked( app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);Copy the code
Get a processRecord object and convert the target activity’s Compoment to a string for easy transfer.
Finally, there is a startProcessLocked method called to continue processing the start of the new process.
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
int uid = app.uid; int[] gids = null; ... / / to find the target application corresponding permissions from PMS service group final IPackageManager PM = AppGlobals. GetPackageManager (); permGids = pm.getPackageGids(app.info.packageName, app.userId); ... gids = new int[permGids.length + 2]; System.arraycopy(permGids, 0, gids, 2, permGids.length); ... app.gids = gids; ... if (entryPoint == null) entryPoint = "android.app.ActivityThread"; Call the Process of starting a new static method Process. The Process ProcessStartResult startResult = Process. The start (entryPoint, app. ProcessName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, app.info.dataDir, entryPointArgs);Copy the code
The main thing this method does is call the static methods of Process to start a new Process. To start a new process, the Zygote process will fork a new child process, after the child process is created. The classLoader loads the ActivityThread class and creates an ActivityThread instance, reflecting the main method that calls ActivityThread.
The ActivityThread main thread is now started in the new process.
Next, look at the Main method of ActivityThread, which is now running in the new process. Let’s look at the Main method of ActivityThread.
23. ActivityThread.main
public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
Copy the code
}
The main job of this method is to create a message loop queue by calling Looper. PrepareMainLooper. Looper.loop is then called to enter the message loop, and the current thread enters the message loop. Make the current thread the main thread of the new process, and then create an ActivityThread object. Call the Attach method.
24. ActivityThread.attach
final ApplicationThread mAppThread = new ApplicationThread();
private void attach(boolean system) { sCurrentActivityThread = this; mSystemThread = system; if (! System) {… final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) {// Ignore}…… } else { }
When the AMS service is started. Initial system process execution environment. At that time, the system parameter was passed as true, indicating that it was a system process. This time, it is a normal application process, so the system parameter is false.
ActivityManagerNative. GetDefault () method for AMS agent, call attachApplication method sends a interprocess communication request, will create ApplicationThread object is passed to the AMS service.
ApplicationThread is an ActivityThread native binder object. Binder servers pass binder objects to AMS services in ActivityThread. The AMS service saves its proxy, and AMS gets the means to communicate with the new process.
The previous code is in the newly created process. That is, the process where App2 is applied, and then through interprocess communication, the following code enters the AMS service again.
25. AMS.attachApplication
ApplicationInfo appInfo = app.instrumentationInfo ! = null ? app.instrumentationInfo : app.info; // Interprocess calls: Call the new process of bindApplication method thread. BindApplication (processName, appInfo, will, app. InstrumentationClass profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace, isRestrictedBackupMode || ! normalMode, app.persistent, new Configuration(mConfiguration), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked()); ... If (normalMode) {try {/ / call the ActivityStackSupervisor method to start a new Activity if (mStackSupervisor. AttachApplicationLocked (app)) { didSomething = true; }}... }Copy the code
After obtaining the PROCESS ID, the attachApplicationLocked method is invoked to continue plication.
The attachApplicationLocked methods mainly work
First look up the ProcessRecord for the process based on the process ID from mPidsSelfLocked. The ProcessRecord is the object of the new process. It just doesn’t point to any process before, because the new process hasn’t been created yet. The new process has now been created. So you need to point it to the new process.
Query the new process-related ContentProvider information from the PMS service. The request is then made through interprocess communication. Call the bindApplication method of Thread. This Thread is the proxy Binder object for the new process’s ApplicationThread. This finally calls the handleBindApplication method of the ActivityThread in the new process. To further handle the initialization of the execution environment for the new process. Mainly new process Application initialization, Instrumentation initialization and installation of related ContentProvider.
Hand over to the ActivityStackSupervisor to continue processing the attchApplication logic.
26. ActivityStackSuperVisor. attachApplicationLocked
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException { final String processName = app.processName; ActivityStack for (int displayNdx = mactivitydisplay.size () -1; displayNdx >= 0; –displayNdx) { ArrayList stacks = mActivityDisplays.valueAt(displayNdx).mStacks; for (int stackNdx = stacks.size() – 1; stackNdx >= 0; –stackNdx) { final ActivityStack stack = stacks.get(stackNdx); if (! isFrontStack(stack)) { continue; } / / find at the top of the stack ActivityRecord ActivityRecord hr = stack. TopRunningActivityLocked (null); if (hr ! = null) { if (hr.app == null && app.uid == hr.info.applicationInfo.uid && processName.equals(hr.processName)) { try { // Call realstartActivityLocked to start the target Activity if (realstartActivityLocked (hr, app, true, true)) {didSomething = true; }} catch (RemoteException e) {… }}
The main job of this method is to walk through and find the target stack, and then get the ActivityRecord at the top of the ActivityStack stack, which is the target Activity that we put at the top of the stack. After you get the information about the Activity to start, you’ve done all this preparatory work to actually start the new Activity.
Step 4: Start Activity B
27. ActivityStackSuperVisor. realStartActivityLocked
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, Boolean checkConfig) throws RemoteException {…… // Save information about the new process to the app variable of ActivityRecord.
Task final TaskRecord Task = r.task; Final ActivityStack Stack = task.stack; // cross-process call, Notice the target process to start the Activity app. Thread. ScheduleLaunchActivity (new Intent (r.i ntent), state Richard armitage ppToken, System. IdentityHashCode (r), r.info, new Configuration(mService.mConfiguration), new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, ! andResume, mService.isNextTransitionForward(), profilerInfo);Copy the code
This method first points the ActivityRecord app object to the new process. The ActivityRecord is then associated with the new process.
The target process ApplicationThread proxy Binder object then initiates an interprocess communication request that calls the target process’s scheduleLaunchActivity method to start a new Activity.
Here the code enters the target process again through a cross-process call.
28. ApplicationThread. scheduleLaunchActivity
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, The Configuration curConfig,… ProfilerInfo profilerInfo) {
updateProcessState(procState, false); ActivityClientRecord r = new ActivityClientRecord(); ActivityClientRecord r = new ActivityClientRecord(); r.token = token; r.ident = ident; ... SendMessage (h.launch_activity, r); }Copy the code
Call to ApplicationThread inside ActivityThread. This ApplicationThread implements ApplicationThreadNative. This enables the Binder server side for interprocess communication. Binder interprocess communication is not explained in detail
Finally, the message is sent, and the HandMessage is handled by the Handler.
The handleMessage calls the handleLaunchActivity of the ActivityThread.
29. ActivityThread.handleLaunchActivity
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// Call the performLaunchActivity method to start the Activity Activity a = performLaunchActivity(r, customIntent); // After the Activity starts. Call handlResumeActivity to resume the Activity if (a! = null) { r.createdConfig = new Configuration(mConfiguration); Bundle oldState = r.state; handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed);Copy the code
…
First call performLaunchActivity to create an Activity object, then call the onCreate method of the Activity to start the Activity, and then call the handleResumeActivity method. When an Activity is activated, it is counted in the resume state.
30. ActivityThread.performLaunchActivity
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); ComponentName Component = r.i.nten.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); }
Call the Instrumentation class to create an Activity object based on the Activity information Activity Activity = null; java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); // Call the attach method to initialize the Activity if (Activity! = null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); 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); ... / / call the Activity's onCreate method mInstrumentation. CallActivityOnCreate (Activity, r.s Tate); ... // Run the Activity's onStart method if (! r.activity.mFinished) { activity.performStart(); r.stopped = false; }... // Call the Activity's onRestoreInstancestate method if (r.state! = null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); ... / / call the Activity of onPostCreate method mInstrumentation. CallActivityOnPostCreate (Activity, r.s Tate); ... // Bind the corresponding ActivityClientRecord with binder to mActivities.put(r.token, r);Copy the code
In the new process, an ActivityClientRecord object is created based on the information passed in from AMS. This object corresponds to an ActivityRecord in the AMS service.
In this method. Based on the ActivityClientRecord of the target Activity B, the Instrumentation class is finally called to create a Acitivity by loading the corresponding Activity class into the ClassLoader. Create an object with reflection.
After creating a new Activity object, the Activity B object, call its onCreate method, which will be called to load the custom user interface, as well as other initialization methods.
After the onCreate method is called. Then, in turn, calls the Activity onStart, onRestoreInstanceState, onPostCreate method, etc. This is the logic that runs the Activity lifecycle.
That’s it. ActivityB is now up and running, which means that the application in which ActivityB is located is also up and running.
Part of the content of the article is arranged from the Internet to query, convenient memory and learning.
The article reference links: blog.csdn.net/lqbz456/art… The article reference links: blog.csdn.net/wuseyukui/a… The article reference links: blog.csdn.net/u012267215/…