ActivityManagerService
ActivityManagerService(AMS) is one of the core Binder services for Android. But in fact AMS management and maintenance of the system of the four components (Activity, Service, Broadcast, ContentProvider) related work, this is very important and complicated task, single in KK is AMS a class source code line number is as high as more than sixteen thousand lines, You can see the scale of the task inside. Of these, AMS has the most complex task for managing activities, which is probably why it is called ActivityManagerService. AMS actually manages activities through three classes, namely ActivityRecord, TaskRecord and ActivityStack. It will be very helpful for us to understand the relationship between them. Therefore, this paper will introduce the relationship and role of the three in AMS.
ActivityRecord
ActivityRecord is the application layer Activity component represented in AMS. For every Activity started in an application, there is an ActivityRecord instance in AMS. The ActivityRecord is created with the Activity starting and destroyed with the Activity ending.
Class structure specification
As for the ActivityRecord class, it is a simple class that does not inherit from any other parent class. It stores information about the Activity component inside it, so the class structure is relatively simple. Here are some of its members:
/** * An entry in the history stack, representing an activity. */
final class ActivityRecord {
final ActivityManagerService service; // owner
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
final int userId; // Which user is this running for?
final Intent intent; // the original intent that generated us
final ComponentName realActivity; // the intent component, or target of an alias.
final String shortComponentName; // the short component name of the intent
final String resolvedType; // as per original caller;
final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String taskAffinity; // as per ActivityInfo.taskAffinity.int labelRes; // the label information from the package mgr.
int icon; // resource identifier of activity's icon.
int logo; // resource identifier of activity's logo.
int theme; // resource identifier of activity's theme.
int realTheme; // actual theme resource we will use, never 0.
int windowFlags; // custom window flags for preview window.
TaskRecord task; // the task this is in.
Configuration configuration; // configuration activity was last running in
CompatibilityInfo compat;// last used compatibility mode
ActivityRecord resultTo; // who started this entry, so will get our reply
final String resultWho; // additional identifier for use by resultTo.
final int requestCode; // code given by requester (resultTo)
HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
ArrayList<Intent> newIntents; // any pending new intents for single-top mode
ActivityOptions pendingOptions; // most recently given options
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
UriPermissionOwner uriPermissions; // current special URI access perms.
ProcessRecord app; // if non-null, hosting application
ActivityState state; // current state we are in.int launchMode; // the launch mode activity attribute.
}
Copy the code
As you can see, ActivityRecord contains a lot of information. Let’s briefly describe it:
- AppToken: A Binder and server-side entity, Token is a Token that is used as an identifier. The appToken is used to identify the AcitvityRecord on the WindowManagerService side. The Token is implemented as follows
static class Token extends IApplicationToken.Stub {
final WeakReference<ActivityRecord> weakActivity;
Token(ActivityRecord activity) {
weakActivity = new WeakReference<ActivityRecord>(activity);
}
@Override public void windowsDrawn(a) {
ActivityRecord activity = weakActivity.get();
if(activity ! =null) { activity.windowsDrawn(); }}@Override public void windowsVisible(a) {
ActivityRecord activity = weakActivity.get();
if(activity ! =null) { activity.windowsVisible(); }}@Override public void windowsGone(a) {
ActivityRecord activity = weakActivity.get();
if(activity ! =null) { activity.windowsGone(); }}... }Copy the code
As you can see, appkToken is actually a Token that implements a method from iApplicationToken.stub, with a weak internal reference to the ActivityRecord instance that contains it. Mark an ActivityRecord on the WMS side and communicate with AMS using this token.
- LaunchedFromUid and launchedFromPackage launch the application UID and package name of the activity, for example, the application activity we start from the launcher, which corresponds to the UID and package name of the launcher
- Intent and ComponentName IntEnts are intents that are passed at startup. ComponentName marks the acitivity component that is started.
- PackageName and processName respectively represent the packageName and processName of the application to which the activity belongs
- TaskAffinity marks the activity’s parent property, which by default is the package name. This member is associated with the TaskRecord to which the activity belongs, as described below.
- Logo and Theme These are the logo and theme resource tags used by the activity.
- The Task application belongs to TaskRecord, which is described later.
- ResultTo and requestCode, resultTo is also an ActivityRecord, which is the activity that the activity corresponding to the current ActivityRecord should return and resume. When we start an activity using startActivityForResult, we usually need to receive the return value from the started activity. This resultTo is the ActivityRecord corresponding to the activity that handles the return value
- App is a ProcessRecord that describes the process of the application in which the activity is currently running
- State Is an enumeration of AcitivityState that describes the current state of an activity that is used internally by AMS. ActivityState is defined as follows:
enum ActivityState {
INITIALIZING,
RESUMED,
PAUSING,
PAUSED,
STOPPING,
STOPPED,
FINISHING,
DESTROYING,
DESTROYED
}
Copy the code
- LaunchMode Indicates the launching mode of the activity
ActivityRecord creation
final int startActivityLocked(IApplicationThread caller,
Intent intent, String resolvedType, ActivityInfo aInfo, IBinder resultTo,
String resultWho, int requestCode,
int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options,
boolean componentSpecified, ActivityRecord[] outActivity) {...// Create ActivityRecord for the activity
ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
intent, resolvedType, aInfo, mService.mConfiguration,
resultRecord, resultWho, requestCode, componentSpecified, this); . }Copy the code
AcitivtyRecord is created when an activity is started. This is done in ActivityStackSupervisor, which we’ll talk about later. Let’s look at the ActivityRecord constructor
ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
ActivityRecord _resultTo, String _resultWho, int _reqCode,
boolean _componentSpecified, ActivityStackSupervisor supervisor) {
service = _service;
appToken = new Token(this);// Create an appToken that marks the activity and holds a weak reference to the ActivityRecord inside the token
info = aInfo;
launchedFromUid = _launchedFromUid;
launchedFromPackage = _launchedFromPackage;
userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
intent = _intent;
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
componentSpecified = _componentSpecified;
configuration = _configuration;
resultTo = _resultTo;
resultWho = _resultWho;
requestCode = _reqCode;
state = ActivityState.INITIALIZING;
frontOfTask = false;
launchFailed = false;
stopped = false;
delayedResume = false;
finishing = false;
configDestroy = false;
keysPaused = false;
inHistory = false;
visible = true;
waitingVisible = false;
nowVisible = false;
thumbnailNeeded = false;
idle = false;
hasBeenLaunched = false; mStackSupervisor = supervisor; . }Copy the code
In the constructor, an appToken for ActivityRecord is created, which we’ve already covered. The initial state of the activity for ActivityState. The INITIALIZING the visible marker here is the activity in the WMS side whether you need the corresponding window display, in constructing ActivityRecord said we’re going to start the activity, In this case, WMS is required to display the window for us, setting true. NowVisible: Indicates the current window display state of the activity. If the activity is still being created, set it to false.
TaskRecord
A TaskRecord is a Task Stack, or back Stack. A Task is a group of activities that interact with the user while performing a specific Task. For example, in order to complete the Task of sending messages through wechat, we need to open the contact list on the home page, and then select a contact to enter the chat interface to send messages. These two activities can be said to be one Task. However, if we say that we want to send pictures, we will also open the activity that selects photos. In this case, a Task is three activities, so a Task is a dynamic concept. However, we always think of it as a collection of activities. In AMS, this collection is called a TaskRecord, which is similar to the Stack structure in data structure. When we open an Activity1 in our App, its corresponding ActivityRecord is added to the TaskRecord, and then we open the second page activity2 in Activity2 and Activity3 in Activity2. Their ActivityRecord is also added to the TaskRecord. When we press the Back button, Activity3 pops up from the TaskRecord, and so on. When all activities in the task are ejected, the task is destroyed. Its final structure is shown below:
Task switching
In general, we can view an app as A TaskRecord. When we open two pages A and B of APP A successively, there will be two ActivityRecord records in the TaskRecord. When we press the home button, Then open the C and D pages of app B, at this time app A’s TaskRecord is in the background, its two activities are stopped, and App B’s TaskRecord is in the foreground and user interaction state. There are two ActivityRecord records in the TaskRecord. If we press the home button again and click on App A, then we find app A stays on page B, which means that app A’s TaskRecord as A whole has switched from background to foreground. It’s in the same state as before. The system can manage multiple TaskRecords in the background at the same time, but if the user has too many apps open in the background, the system may reclaim some of them.
When I open multiple apps, there are multiple TaskRecords in AMS, and we can switch between these TaskRecords. When I open the Email application, I can open the photo album application to select pictures in the process of editing the Email, and then go back to the Email to edit the Email. This is the convenience of Task switching.
launchMode
Android manages activities by using a fifo stack in the same Task. By default, when we create an activity, we have an instance in TaskRecord. But sometimes we might want to use an existing instance of TaskRecord when we start an activity or we might want to place it in a separate TaskRecord when we start an activity, LaunchMode is associated with TaskRecord. The launchMode is associated with TaskRecord.
There are four different boot modes:
- Standard, which is the default startup mode of Android, creates an instance for each Activity that is started in a Task. You can have multiple instances of the same Activity in a Task.
- SingleTop: If an instance of an activity is already at the top of the Task stack, it is reused when the activity is started again rather than creating a new instance, and the instance is passed to the Intent via onNewIntent. For example, if the Task already has four activityA->B->C->D and D is at the top of the Task, it will still be A->B->C->D when the D page is opened again. In Standard mode, it will be A->B->C->D
- An activity instance can only have one instance in a Task. If an activity instance already exists in the Task, Android routes the Intent directly to the existing activity instance in the Task. This is handled by calling onNewIntent() instead of creating a new Activity instance object. And it clears all other activities on top of it. However, if the instance does not exist in the Task, the system will compare the taskAffnity of the calling activity and the called activity. If the instance is consistent, the system will create an instance of the activity and add it to the existing Task. If the instance is inconsistent, Create a new Task for the activity as the root activity of the Task. In fact, the Task affinity is determined by the Task’s root activity.
- SingleInstance, if this mode is set, there can only be one instance of the activity in the Task.
Not only can the launch mode be set in the AndroidManifest, but it can also be set using Intent Flags. Some common Flags can be used to handle the relationship between activities and tasks more conveniently and flexibly:
- FLAG_ACTIVITY_NEW_TASK, which is the same as singleTask
- FLAG_ACTIVITY_SINGLE_TOP, this flag is the same as singleTop
- FLAG_ACTIVITY_CLEAR_TOP: If an instance of an activity is already in the current Task, it will be destroyed on restart. For example, Task A->B->C->D, if this flag is added to Task B in D, then both C and D will be destroyed from Task A->B->C->D, if B is the default startup mode, then the instance of B will be destroyed and A new instance will be added to the top of Task. If we do not want it to be created again, This can be used with FLAG_ACTIVITY_SINGLE_TOP to reuse the top INSTANCE of B and trigger onNewIntent.
- FLAG_ACTIVITY_REORDER_TO_FRONT, the newly started Activity will be placed at the top of the task to which it belongs. For example, when Task A->B->C->D starts task B with this flag, task B will be placed at the top of the task. The order of tasks is now A,C,D, and B.
- FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET this flag will clear up the Activity when the TASK is reset. For example, TASK A is currently A->B, TASK A is started and TASK B is added, TASK A becomes A and TASK B is cleared when switching to Home and opening the application. This sets a restore point in the Task and restores the Task to the specified restore point when the Task is reset.
AndroidManifest also allows you to set Task related attributes.
- android:allowTaskReparenting
This property defines the activity’s ability to reparent Task, indicating whether an activity instance can be moved from the Task that started it to a Task that has a common TaskAffinity after the current application has faded into the background. “true” means it can be moved. “False” means that it must stay in the current application task. The default value is false. Here is an example to illustrate the situation: For example, App A has one Activity:MainActivity, and App B has two activities :BMainActivity and SecondActivity, where SecondActivity defines this property as true. When we start the SecondActivity of App B in MainActivity in App A, then A’s Task is MainActivity->SecondActivity, Note that the TaskAffinity for MainActivity and SecondActivity are not the same; they are the respective package names. If you press the Home button to go back to the Launcher and then start App B, you’ll find that the page is SecondActivity. That is, SecondActivity moves from the Task of App A to the Task of App B that has the same Task as its TaskAffinity.
- android:clearTaskOnLaunch
If this property is set to true, each time the user restarts the application, only the Root Activity will be seen and all other activities in the Task will be removed from the stack. This property is also valid only for Root activities.
- android:alwaysRetainTaskState
This attribute is used to keep the Task in its original state. True means to keep the Task in its original state. False does not keep the Task in its original state. All activities except Root activities are cleaned up. However, if this property is set to true, the interface of the last operation can be kept.
- android:noHistory
When this property is set to true, the Activity is not recorded in the Task’s history list. That is, the Activity is not visible, and it is effectively destroyed because AMS has no information about it.
The realization of the TaskRecord
Now that you know about Task, let’s look at how it is implemented in AMS.
final class TaskRecord extends ThumbnailHolder {
final int taskId; // Unique identifier for this task. // Unique identifier for task stack
final String affinity; // The affinity name for this task, or null.
Intent intent; // The original intent that started the task.
Intent affinityIntent; // Intent of affinity-moved activity that started this task.
ComponentName origActivity; // The non-alias activity component of the intent.
ComponentName realActivity; // The actual activity component that started the task.
int numActivities; // Current number of activities in this task.
long lastActiveTime; // Last time this task was active, including sleep.
boolean rootWasReset; // True if the intent at the root of the task had
// the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
boolean askedCompatMode;// Have asked the user about compat mode for this task.
String stringName; // caching of toString() result.
int userId; // user for which this task was created
int numFullscreen; // Number of fullscreen activities.
/** List of all activities in the task arranged in history order */
final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>();// All activities in historical order
/** Current stack */
ActivityStack stack;
/** Takes on same set of values as ActivityRecord.mActivityType */
private int mTaskType;
/** Launch the home activity when leaving this task. */
boolean mOnTopOfHome = false;
TaskRecord(int_taskId, ActivityInfo info, Intent _intent) { taskId = _taskId; affinity = info.taskAffinity; setIntent(_intent, info); }... }Copy the code
In AMS, tasks are described by the TaskRecord class, which has fewer members than ActivityRecord.
- TaskId, which is a unique tag for TaskRecord
- Affnity, the name of the parent attribute, and the Task’s ativity have the same taskAffinity, which is determined by the taskAffinity of the Task’s Root Activity.
- Intent: the Intent that starts the Task’s source activity, that is, the Intent that starts the root activity.
- NumActivities, which is the number of activities in the current Task.
- NumFullscreen Number of full-screen activities
- MActivities maintains an ActivityRecord instance, which represents an Activity. The TaskRecord stack structure is represented by an ArrayList.
- Stack, which belongs to ActivityStack, which we’ll talk about later
The creation of TaskRecord
In general, AMS creates a TaskRecord stack for the first activity of the App when it is started. Of course, it is possible to create a new TaskRecord after it is started. For example, we start the activity of singleTask. In addition, a taskAffinity is specified for the Activity that is different from the package name. In this case, a new TaskRecord is also created for the Activity. It is possible for an App to have multiple TaskRecords, depending on the application’s usage scenario and requirements.
Let’s follow the logic of the analysis to see how creation for TaskRecord is implemented in AMS.
final int startActivityUncheckedLocked(ActivityRecord r,
ActivityRecord sourceRecord, int startFlags, boolean doResume,
Bundle options) {
if(((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) ! =0 &&
(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
boolean addingToTask = false;
TaskRecord reuseTask = null; . ActivityRecord intentActivity = r.launchMode ! = ActivityInfo.LAUNCH_SINGLE_INSTANCE ? findTaskLocked(r) : findActivityLocked(intent, r.info); .if (r.resultTo == null&&! addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) ! =0) {...if (reuseTask == null) { r.setTask(targetStack.createTaskRecord(getNextTaskId(), newTaskInfo ! =null? newTaskInfo : r.info, newTaskIntent ! =null ? newTaskIntent : intent,
true), null.true);// Create a new taskRecord}}}}Copy the code
When launch mode is set to singleTask, we find TaskRecord in findTaskLocked to find the TaskRecord for the Activity launched. If we find it, we return the corresponding ActivityRecord for the Activity at the top of it.
ActivityRecord findTaskLocked(ActivityRecord r) {
if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + r);
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
if(! r.isApplicationActivity() && ! stack.isHomeStack()) {if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: " + stack);
continue;
}
final ActivityRecord ar = stack.findTaskLocked(r);
if(ar ! =null) {
returnar; }}if (DEBUG_TASKS) Slog.d(TAG, "No task found");
return null;
}
/** * Returns the top activity in any existing task matching the given * Intent. Returns null if no such task is found. * /
ActivityRecord findTaskLocked(ActivityRecord target) {
Intent intent = target.intent;// The Intent to start the Activity
ActivityInfo info = target.info;
ComponentName cls = intent.getComponent();
if(info.targetActivity ! =null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + target + " in " + this);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {/ / traverse TaskRecord
final TaskRecord task = mTaskHistory.get(taskNdx);
if(task.userId ! = userId) {// Looking for a different task.
if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": different user");
continue;
}
final ActivityRecord r = task.getTopActivity();// Get the activity at the top of the taskRecord
if (r == null|| r.finishing || r.userId ! = userId || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": mismatch root " + r);
continue;
}
if (DEBUG_TASKS) Slog.d(TAG, "Comparing existing cls="
+ r.task.intent.getComponent().flattenToShortString()
+ "/aff=" + r.task.affinity + " to new cls="
+ intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
if(task.affinity ! =null) {TaskAffinity is matched by the parent property taskAffinity
if (task.affinity.equals(info.taskAffinity)) {
if (DEBUG_TASKS) Slog.d(TAG, "Found matching affinity!");
returnr; }}else if(task.intent ! =null && task.intent.getComponent().equals(cls)) {// Otherwise the top of the stack activity is started
if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
//dump();
if (DEBUG_TASKS) Slog.d(TAG, "For Intent " + intent + " bringing to top: "
+ r.intent);
return r;
} else if(task.affinityIntent ! =null
&& task.affinityIntent.getComponent().equals(cls)) {
if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
//dump();
if (DEBUG_TASKS) Slog.d(TAG, "For Intent " + intent + " bringing to top: "
+ r.intent);
return r;
} else if (DEBUG_TASKS) {
Slog.d(TAG, "Not a match: "+ task); }}return null;// Return null if not found
}
Copy the code
In findTaskLocked, we compare the Activity to be started with the Affinity of the Task and return the ActivityRecord at the top of the TaskRecord if a match is found, or null if none is found. If the return value intentActivity is null, then neither addingToTask nor reuseTask will be set, and the TaskRecord will be created with createTaskRecord and set to ActivityRecord.
The reuse of TaskRecord
In fact, in most cases, activities will reuse TaskRecord, that is, activities will be added to the same TaskRecord, except when the application is launched for the first time or the taskAffinity is different. If the intentActivity returned from findTaskLocked in the previous step is not null, this means that we have found a reusable TaskRecord for an activity that starts in singleTask mode.
final int startActivityUncheckedLocked(ActivityRecord r,
ActivityRecord sourceRecord, int startFlags, boolean doResume,
Bundle options) {
if(((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) ! =0 &&
(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0) || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { ActivityRecord intentActivity = r.launchMode ! = ActivityInfo.LAUNCH_SINGLE_INSTANCE ? findTaskLocked(r) : findActivityLocked(intent, r.info);if(intentActivity ! =null) {// Find the ActivityRecord corresponding to the top of the TaskRecord stack
if (r.task == null) {
r.task = intentActivity.task;// Set r's TaskRecord to this Task}}}if (r.resultTo == null&&! addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) ! =0) {... }else if(sourceRecord ! =null) {// Reuse TaskRecord for other startup modesTaskRecord sourceTask = sourceRecord.task; targetStack = sourceTask.stack; . r.setTask(sourceTask, sourceRecord.thumbHolder,false); }}Copy the code
ActivityStack
ActivityStack may sound confused with the Task stack. Many people’s first reaction to ActivityStack is that it is our Task stack, but this is not the case. We know that there may be multiple tasks in the system. AMS uses ActivityStack to manage tasks easily. Within ActivityStack, a set of TaskRecords are maintained using an ArrayList. In general, the Launcher Task belongs to a separate ActivityStack, called the Home ActivityStack, and the System UI, such as rencentActivity Task, belongs to a separate ActivityStack. Tasks of other apps belong to another ActivityStack.
structure
Let’s take a look at ActivityStack’s structure
final class ActivityStack {...final ActivityManagerService mService;/ / AMS references
final WindowManagerService mWindowManager;/ / WMS reference
/** * The back history of all previous (and possibly still * running) activities. It contains #TaskRecord objects. */
private ArrayList<TaskRecord> mTaskHistory = new ArrayList<TaskRecord>();
/** * List of running activities, sorted by recent usage. * The first entry in the list is the least recently used. * It contains HistoryRecord objects. * /
final ArrayList<ActivityRecord> mLRUActivities = newArrayList<ActivityRecord>(); ./** * This is the last activity that we put into the paused state. This is * used to determine if we need to do an activity transition while sleeping, * when we normally hold the top activity paused. */
ActivityRecord mLastPausedActivity = null;// Records the Activity that was paused after the last ActivityStack switch
/** * Current activity that is resumed, or null if there is none. */
ActivityRecord mResumedActivity = null;// The currently running activity, which can be null to indicate that the currently running activity is not in the ActivityStack
final int mStackId;
/** Run all ActivityStacks through this */
final ActivityStackSupervisor mStackSupervisor;/ / the ActivityStack steward. }Copy the code
ActivityStack maintains a set of TaskRecords, a set of recently used Activities, and Activities that were suspended the last time ActivityStack switched, similar to TaskRecord. Multiple Activitystacks can also be switched. For example, when we click on the app from the desktop Launcher, we will switch from the ActivityStack of the Launcher to the ActivityStack of the app. MResumedActivity indicates a running activity. It can also be null, which indicates that the currently running activity is not in the ActivityStack. Similarly, an ActivityStack is uniquely identified by the mStackId in ActivityStack.
Now that you understand the concept and purpose of ActivityStack, let’s look at how ASM manages it. These Activitystacks are managed by Activitystacks supervisor in AMS, which indirectly manages TaskRecords. Manage all activitystacks inside the ActivityStackSupervisor through mHomeStack and mStacks, where mHomeStack is the ActivityStack of the Launcher’s TaskRecord. MStacks is another ActivityStack, which is an ArrayList.
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor.BatteryStatsImpl.BatteryCallback {.../** Run all ActivityStacks through this */ActivityStackSupervisor mStackSupervisor; .// Call the main method when SystemServer starts
public static final Context main(int factoryTest) {... ActivityManagerService m = thr.mService; mSelf = m; ActivityThread at = ActivityThread.systemMain(); mSystemThread = at;// Create an ActivityThread for AMS
Context context = at.getSystemContext();
context.setTheme(android.R.style.Theme_Holo);// The system default context theme
m.mContext = context;
m.mFactoryTest = factoryTest;
m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface(a));
m.mStackSupervisor = new ActivityStackSupervisor(m, context, thr.mLooper);// Create the ActivityStack managerm.mBatteryStatsService.publish(context); m.mUsageStatsService.publish(context); m.mAppOpsService.publish(context); .returncontext; }}Copy the code
public final class ActivityStackSupervisor {
final ActivityManagerService mService;
/** Identifier counter for all ActivityStacks */
private int mLastStackId = HOME_STACK_ID;
/** Task identifier that activities are currently being started in. Incremented each time a * new task is created. */
private int mCurTaskId = 0;
/** The current user */
private int mCurrentUser;
/** The stack containing the launcher app */
private ActivityStack mHomeStack;
/** The non-home stack currently receiving input or launching the next activity. If home is * in front then mHomeStack overrides mFocusedStack. * DO NOT ACCESS DIRECTLY - It may be null, use getFocusedStack() */
private ActivityStack mFocusedStack;
/** All the non-launcher stacks */
private ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
/** List of activities that are waiting for a new activity to become visible before completing * whatever operation they are supposed to do. */
final ArrayList<ActivityRecord> mWaitingVisibleActivities = new ArrayList<ActivityRecord>();
/** List of activities that are ready to be stopped, but waiting for the next activity to * settle down before doing so. */
final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<ActivityRecord>();
/** List of activities that are ready to be finished, but waiting for the previous activity to * settle down before doing so. It contains ActivityRecord objects. */
final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<ActivityRecord>();
/** List of activities that are in the process of going to sleep. */
final ArrayList<ActivityRecord> mGoingToSleepActivities = newArrayList<ActivityRecord>(); . }Copy the code
- MLastStackId is the count of all activitystacks that are used to calculate the stackId when creating activitystacks
- MCurTaskId refers to the stackId corresponding to the ActivityStatck of the current foreground
- MHomeStack This is the ActivityStack of the Launcher’s TaskRecord.
- MStacks All non-launcher ActivityStack. This is an ArrayList, because there can be multiple activitystacks that are not Launcher.
- In addition to the above members, ActivityStackSupervisor maintains a collection of ActivityRecords for activities that are waiting to be displayed, or are being stopped or destroyed.
The creation process
The same ActivityStack is created on demand
final int startActivityUncheckedLocked(ActivityRecord r,
ActivityRecord sourceRecord, int startFlags, boolean doResume,
Bundle options) {...if (r.resultTo == null&&! addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) ! =0) { targetStack = adjustStackFocus(r); }... }Copy the code
In fact, ActivityStack is created at the start of the Launcher, and it is the first app that is launched after the system starts. When the app starts the first Activity, AMS does not have an ActivityStack that is not Launcher, so you need to create one. Later apps will find this ActivityStack when they start. AdjustStackFocus method of ActivityStackSupervisor. This method looks for ActivityStack (mFocusedStack) for Acitivity to boot.
//frameworks/base/services/java/com/android/server/am/ActivityStackSupervisor.java
ActivityStack adjustStackFocus(ActivityRecord r) {
final TaskRecord task = r.task;
if(r.isApplicationActivity() || (task ! =null && task.isApplicationTask())) {
if(task ! =null) {
final ActivityStack taskStack = task.stack;
if(mFocusedStack ! = taskStack) {if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
"adjustStackFocus: Setting focused stack to r=" + r + " task=" + task);
mFocusedStack = taskStack.isHomeStack() ? null : taskStack;
} else {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
"adjustStackFocus: Focused stack already=" + mFocusedStack);
}
return taskStack;
}
// Return a Focused stack if it already exists
if(mFocusedStack ! =null) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
"adjustStackFocus: Have a focused stack=" + mFocusedStack);
return mFocusedStack;
}
for (int stackNdx = mStacks.size() - 1; stackNdx > 0; --stackNdx) {
ActivityStack stack = mStacks.get(stackNdx);
if(! stack.isHomeStack()) {if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
"adjustStackFocus: Setting focused stack=" + stack);
mFocusedStack = stack;
returnmFocusedStack; }}// Create a Focused Stack if there isn't one
// Time to create the first app stack for this user.
int stackId =
mService.createStack(-1, HOME_STACK_ID, StackBox.TASK_STACK_GOES_OVER, 1.0 f);
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: New stack r=" + r +
" stackId=" + stackId);
mFocusedStack = getStack(stackId);
return mFocusedStack;
}
return mHomeStack;
}
Copy the code
AdjustStackFocus (adjustStackFocus, adjustStackFocus, adjustStackFocus) ActivityStack is returned as mFoucnedStack. Otherwise, if the TaskRecord has not been created and mFocusedStack is not null, the TaskRecord is returned. Otherwise a non-Home Stack will be found from the current mStacks and returned as mFocusedStack, or if none of the above is available, an ActivityStack will be created for the current system and returned as mFocusedStack. This is done by calling AMS’s createStack method to return the stackId as a result.
@Override
public int createStack(int taskId, int relativeStackBoxId, int position, float weight) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"createStack()");
if (DEBUG_STACK) Slog.d(TAG, "createStack: taskId=" + taskId + " relStackBoxId=" +
relativeStackBoxId + " position=" + position + " weight=" + weight);
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
int stackId = mStackSupervisor.createStack();
mWindowManager.createStack(stackId, relativeStackBoxId, position, weight);
if (taskId > 0) {
moveTaskToStack(taskId, stackId, true);
}
return stackId;
} finally{ Binder.restoreCallingIdentity(ident); }}}Copy the code
CreateStack is provided to the consumer as a Binder interface to AMS and is further created through the createStack method of ActivityStackSupervisor.
int createStack(a) {
while (true) {
if (++mLastStackId <= HOME_STACK_ID) {
mLastStackId = HOME_STACK_ID + 1;
}
if (getStack(mLastStackId) == null) {
break;
}
}
mStacks.add(new ActivityStack(mService, mContext, mLooper, mLastStackId));
return mLastStackId;
}
Copy the code
Calculate the ID of the ActivityStack to be created by mLaskStackId, and then add the newly created instance to mStacks.
Task switching
final int startActivityUncheckedLocked(ActivityRecord r,
ActivityRecord sourceRecord, int startFlags, boolean doResume,
Bundle options) {...if(((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) ! =0 &&
(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
// If bring to front is requested, and no result is requested, and
// we can find a task that was started with this same
// component, then instead of launching bring that one to the front.
if (r.resultTo == null) {
// See if there is a task to bring to the front. If this is
// a SINGLE_INSTANCE activity, there can be one and only one
// instance of it in the history, and it is always in its own
// unique task, so we do a special search.ActivityRecord intentActivity = r.launchMode ! = ActivityInfo.LAUNCH_SINGLE_INSTANCE ? findTaskLocked(r) : findActivityLocked(intent, r.info);if(intentActivity ! =null) {// Find the ActivityRecord corresponding to the top of the TaskRecord stack
if (r.task == null) {
r.task = intentActivity.task;// Set r's TaskRecord to this Task
}
targetStack = intentActivity.task.stack;// Set ActivityStack to the Stack.// If the target task is not in the front, then we need
// to bring it to the front... except... well, with
// SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
// to have the same behavior as if a new instance was
// being started, which means not bringing it to the front
// if the caller is not itself in the front.
final ActivityStack lastStack = getLastStack();
ActivityRecord curTop = lastStack == null?
null : lastStack.topRunningNonDelayedActivityLocked(notTop);// Fetch the ActivityRecord at the top of it
if(curTop ! =null&& (curTop.task ! = intentActivity.task || curTop.task ! = lastStack.topTask())) {// Compare the current task with the reused task. If the reused task is not the same, move the reused task to the front end
r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
if (sourceRecord == null|| (sourceStack.topActivity() ! =null &&
sourceStack.topActivity().task == sourceRecord.task)) {
// We really do want to push this one into the
// user's face, right now.
movedHome = true; targetStack.moveTaskToFrontLocked(intentActivity.task, r, options); . }}}}}... }Copy the code
Task switching is also done in startActivityUncheckedLocked, first of all, we through findTaskLocked for the activity to start looking for a suitable TaskRecord, through initActivity logo, Use getLastStack to get the ActivityStack that you switched to last time, and then get the ActivityRecord at the top of it. This is the non-launcher Stack, and the Task at the top of it may not be the one marked by initAcitivty, because maybe the TaskRecord of the app we started earlier is different from that of curtop.task! Intentactivity_brought_to_front = intentactivity. task = intentactivity. task = intentactivity_brought_to_front Also move the Task to the top of ActivityStack with moveTaskToFrontLocked.
final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) {
final int numTasks = mTaskHistory.size();/ / the number of the task
final int index = mTaskHistory.indexOf(tr);// The current tr position in the Stack. mStackSupervisor.moveHomeStack(isHomeStack());// Shift all activities with this task up to the top
// of the stack, keeping them in the same internal order.
insertTaskAtTop(tr);// Move tasks to the top of the Stack. mWindowManager.moveTaskToTop(tr.taskId); mStackSupervisor.resumeTopActivitiesLocked(); . }Copy the code
MoveTaskToFrontLocked moves the TaskRecord to the top with insertTaskAtTop
private void insertTaskAtTop(TaskRecord task) {
// If this is being moved to the top by another activity or being launched from the home
// activity, set mOnTopOfHome accordingly.
ActivityStack lastStack = mStackSupervisor.getLastStack();
final boolean fromHome = lastStack == null ? true : lastStack.isHomeStack();
if(! isHomeStack() && (fromHome || topTask() ! = task)) { task.mOnTopOfHome = fromHome; } mTaskHistory.remove(task);/ / delete first
// Now put task at top.
int stackNdx = mTaskHistory.size();
if(task.userId ! = mCurrentUser) {// Push non-current user tasks to the end of the current task
// Put non-current user tasks below current user tasks.
while (--stackNdx >= 0) {
if(mTaskHistory.get(stackNdx).userId ! = mCurrentUser) {break;
}
}
++stackNdx;
}
mTaskHistory.add(stackNdx, task);
}
Copy the code
InsertTaskAtTop removes the TaskRecord from mTaskHistory, calculates the location, stackNdx, and finally adds it to mTaskHistory, where the top Task is stored at the end.
conclusion
From the above analysis, we know the relationship between ActivityRecord, TaskRecord and ActivityStack. The organization relationship between ActivityRecord, TaskRecord and ActivityStack is actually contained relationship, that is, AcitivtyStack contains the TaskRecord collection. A TaskRecord contains a collection of ActivityRecords and, in reverse, an ActivityRecord records the TaskRecord to which it belongs, while a TaskRecord records the ActivityStack to which it belongs. Among them, ActivityRecord is the existence form of an Activity component in AMS, and it corresponds to an Activity instance (note that the instance is described here). A TaskRecord is used to describe a Task, which is a group of activities organized in a stack. Each initiated Activity (ActivityRecord) should have a TaskRecord to which it belongs. ActivityStack is used to manage TaskRecords in the system. There may be multiple groups of Activitystacks in the system, which can be divided into Launcher Activitack and non-Launcher ActivityStack. The TaskRecord for the App we launched is managed by the non-Launcher ActivityStack, which is created when the system launched the first App.
The above is a personal understanding, there are improper description of the place, please feel free to comment ~