This is the second in the Android background killing series, mainly explaining how ActivityMangerService restores processes killed in the background (based on 4.3). In the opening FragmentActivity and PhoneWindow background killing processing mechanism, some common problems caused by background killing are briefly described, as well as some compatibility of Android system controls with background killing, and the role of onSaveInstance and onRestoreInstance in execution timing. Finally, it talks about how to deal with background killing, but there is no explanation on how to restore processes killed in the background. This article does not involve background killing, such as the LowmemoryKiller mechanism, but only describes how to restore killed processes. Suppose an application is killed in the background, and when the App is called up again from the recent task list, how does the system handle it? There are several problems that may need to be solved:
How does the system (AMS) know that the App is killed? First, let's look at the first question. How does the system know that the Application has been killed? Android uses the Linux oomKiller mechanism, but simply makes a variant of it, using a hierarchical LowmemoryKiller. But this is actually at the kernel level. After LowmemoryKiller kills the process, it will not send a notification to the user space, which means that the ActivityMangerService at the framework layer cannot know whether the App has been killed. However, only by knowing whether the App or Activity has been killed can AMS (ActivityMangerService) correctly follow the wake-up process. So when does AMS know that the App or Activity has been killed in the background? Let's first look at what happens when we wake up from the recent task list. The process of calling up the App again from the recent task list or Icon In the system source code systemUi package, there is a RecentActivity, which is actually the entrance to the recent task list, and its presentation interface is displayed through RecentsPanelView. Click the recent App and its execution code is as follows:
In the above code, there is a judgment ad.taskId >= 0. If this condition is met, the APP is called up through moveTaskToFront. So how is ad.taskId obtained? There are various RecentTasksLoader in the recent package. This class is a Loader used to load the recent task list. Take a look at its source code, mainly at loading:
As you can see, it actually requests the recent task information from AMS through ActivityManger's getRecentTasks, and then creates TaskDescription through createTaskDescription. The recentInfo.id passed here is actually the taskId of TaskDescription. Let's take a look at its meaning:
It can be seen that the id of RecentTaskInfo is determined by TaskRecord. If numActivities > 0 in TaskRecord, go to the Id of TaskRecord, otherwise take -1. The numActivities here is actually the number of ActivityRecords recorded in TaskRecode. For more specific details, you can check ActivityManagerService and ActivityStack by yourself. Then it is easy to explain here. As long as it is a surviving APP or an APP killed by LowmemoryKiller, its AMS ActivityRecord is completely saved. This is the basis for recovery. The data obtained by RecentActivity is actually a replica of the one in AMS. RecentActivity does not know whether the APP to be awakened is alive. As long as TaskRecord tells RecentActivity that it is in stock, it will directly go through the awakening process, that is, awaken the App through ActivityManager's moveTaskToFront. As for the subsequent work, it will be completely handled by AMS. Now take a look at the flowchart here: When calling up the App, AMS detects whether the App or Activity is killed abnormally. Next, look at moveTaskToFrontLocked. This function is in ActivityStack. ActivityStack is mainly used to manage ActivityRecord stack. All started Activities retain an ActivityRecord in ActivityStack, which is also a basis for AMS to manage Activity. ActivityStack finally movesTaskToFrontLocked and calls resumeTopActivityLocked to wake up Activity. AMS obtains the information of the Activity to be resumed mainly through ActivityRecord. It does not know whether the Activity itself is alive. After obtaining it, AMS knows the link of waking up the Activity before knowing that the App or Activity is killed. Take a look at the resumeTopActivityLocked source code:
Since finish is not actively called, AMS will not clean up ActivityRecord and TaskRecord. Therefore, when resuming, the above branch is taken. Next.app.thread.scheduleSendResult or next.app.thread.scheduleResumeActivity may be called here to call up the previous Activity. However, if the APP or Activity is killed by an exception, the call-up operation must fail and an exception will be thrown. First, assuming that the entire APP is killed, the Binder thread that communicates with AMS on the APP side no longer exists. At this time, RemoteException will be thrown when communicating through Binder. In this way, the catch part below will be taken, and the APP will be rebuilt again through startSpecificActivityLocked, and the last Activity will be rebuilt. In fact, you can use AIDL to write a C/S communication locally, close one end, and then access it with the other end, which will throw a RemoteException, as shown below: There is another possibility, the APP is not killed, but the Activity is killed. What will happen at this time? First of all, the management of Activity must be through AMS, and the killing of Activity must be handled by AMS and recorded. Strictly speaking, this situation does not belong to background killing, because it belongs to the normal management of AMS and is within the controllable range. For example, if the "Do not retain activities" in the developer mode is turned on, at this time, although the Activity will be killed, the ActivitRecord is still retained, so there is still a trace to follow when waking up or rolling back. Take a look at the Destroy callback code of ActivityStack.
There are two key points here: 1. Tell the client's AcvitityThread to clear the Activity; 2. If the AMS closes the Activity abnormally, set the state of the ActivityRecord to ActivityState.DESTROYED and clear its ProcessRecord reference: r.app = null. This is an important sign when waking up. Through this, AMS can know that the Activity was closed abnormally by itself. Setting ActivityState.DESTROYED is to avoid the subsequent clearing logic.
Look at the key point 1 of the code. Only when r.state == ActivityState.DESTROYING will ActivityRecord be removed. However, for an Activity that is not finished abnormally, its state will not be set to ActivityState.DESTROYING. It will directly skip ActivityState.DESTROYING and be set to ActivityState.DESTROYED. Therefore, it will not removeActivityFromHistoryLocked, which means that the ActivityRecord scene is retained. It seems that it also relies on exceptions to distinguish whether the Activity is terminated normally. How to start the Activity in this case? Through the above two analysis, we know two key points.
This ensures that when resumeTopActivityLocked, the startSpecificActivityLocked branch is taken
At this point, AMS knows whether the app or activity has been killed abnormally, and thus decides whether to go through the resume process or the restore process. How to save the scene before the App is killed: New Activity startup and old Activity preservation The process of saving the App scene is relatively simple. The entry point is basically when starting Activity. As long as the interface jumps, it basically involves the switching of Activity and the saving of the current Activity scene: Let's draw a simple diagram first. When talking about FragmentActivity in the opening part, I briefly talked about the execution timing of onSaveInstance. Here we will take a closer look at how AMS manages these jumps and scene saving. Simulate the scenario: When Activity A starts Activity B, A is not visible at this time and may be destroyed. It is necessary to save the scene of A. What is this process like: It is briefly described as follows
The process is probably as follows: Now let's follow the source code step by step to see what AMS does when the new Activity is started and the old Activity is saved: skip the simple startActivity and go directly to AMS to see ActivityManagerService
ActivityStack
Here, startActivityMayWait is used to start a new APP or a new Activity. Here we only look at the simple ones. As for the process of starting the App from the desktop, you can refer to more detailed articles, such as Lao Luo's startActivity process, which is probably to create a new ActivityRecord, ProcessRecord, etc., and add the corresponding stack in AMS, etc., resumeTopActivityLocked is the unified entrance for interface switching. When you come in for the first time, since ActivityA is not paused yet, you need to pause ActivityA first. After these are completed, ActivityStack
In fact, here is to pause ActivityA. AMS tells ActivityThread that ActivityA needs to be paused through Binder. After ActivityThread is completed, it notifies AMS through Binder, and AMS will start to resume ActivityB.
ActivityThread
After receiving the pause message from ActivityA, AMS will wake up ActivityB, and the entry is still resumeTopActivityLocked, wake up B, and then stop A further. At this time, the scene preservation is involved. ActivityStack
This article does not care how ActivityB is started. We only care about how ActivityA saves the scene. After ActivityB starts, it stops ActivityA through stopActivityLocked of ActivityStack.
Looking back at the APP side, let's look at the call in ActivityThread: First, callActivityOnSaveInstanceState to save the scene to the Bundle.
Afterwards, ActivityManagerNative.getDefault().activityStopped is used to notify AMS that the Stop action is complete. When notifying, the saved on-site data will also be brought over.
Through the above process, AMS not only starts a new Activity, but also saves the scene of the previous Activity. Even if the previous Activity is killed for various reasons, AMS can still know how to wake up the Activity and restore it during the rollback or re-awakening process. Now we need to solve two problems: 1. How to save the scene, 2. How does AMS determine whether the APP or Activity is killed abnormally? Then the last question is how does AMS restore the APP or Activity that was killed abnormally. Recovery logic when the entire Application is killed in the background In fact, when explaining how AMS determines whether the APP or Activity is killed by an exception, the logic of recovery has been involved. We also know that once AMS knows that the APP is killed by the background, it is not a normal resume process, but a relaucher. Let's first look at how the entire APP is handled when it is killed. Look at the resumeTopActivityLocked part. From the above analysis, it is known that in this scenario, the Binder communication will throw an exception and take the exception branch, as follows:
From the above code, we can know that it is actually startSpecificActivityLocked, which is not much different from the first time you call up the APP from the desktop. There is only one thing to note, that is, the Activity started at this time has the last scene data passed, because the last time when it retreated to the background, all the scenes of the Activity interface were saved and passed to AMS, then this time the recovery startup will return this data to ActivityThread, let's take a closer look at the special processing code for recovery in performLaunchActivity:
Let's look at key points 1 and 2. First, let's look at key point 1. mInstrumentation.callActivityOnCreate will call back onCreate of Activity. This function actually mainly performs some Fragment recovery work for FragmentActivity. r.state in ActivityClientRecord is what AMS transmits to APP to restore the scene. When it starts normally, these are null. Let's look at key point 2. When r.state != null is not empty, mInstrumentation.callActivityOnRestoreInstanceState is executed. This function is mainly used to restore Window by default, such as restoring the previous display position of ViewPager, etc. It can also be used to restore user saved data. The application is not killed by the background, and the activity is killed and restored Turning on the developer mode "Do not retain activities" is such a scenario. In the above analysis, we know that when AMS actively kills the Activity abnormally, the app field of AcitivityRecord is set to empty. Therefore, resumeTopActivityLocked is different from the entire APP being killed, and the following branch will be taken
Although they are slightly different, they still follow the startSpecificActivityLocked process, but they do not create a new APP process. The rest are the same and will not be explained here. At this point, we should understand.
At this point, the logic of ActivityManagerService restoring the APP scene should be explained. Let me mutter some more questions, which may be some interview points.
|
<<: Android Loader Detailed Explanation
>>: 50 Tips, Tricks, and Resources for Android Developers
Seed users generally have an open and adventurous...
The earth, this blue planet, not only dances on i...
hey, Beidou! China's Beidou, the world's ...
On the evening of December 30, 2021, the world re...
Apple is exploring moving 15% to 30% of its hardw...
As Baidu focuses on promoting Baidu APP, it means...
The World Meteorological Organization (WMO) relea...
Last year, Microsoft first introduced the functio...
Although driverless cars are developing rapidly, ...
Q: How can I make my program installation package...
When it comes to online event planning , the most...
appendix: References: Source: A scholar...
1. What is the product? At the end of the day, th...
Recently, a friend left a message to complain: Th...
Open the APP on your phone, no matter which e-com...