在 android 中,AMS用于管理activity的创建,启动,和销毁。
AMS用ActivityStack,TaskRecord,和ActivityRecord三个class来管理所有的activity。
三者的关系是ActivityStack中存放所有的TaskRecord,TaskRecord中存放属于自己的ActivityRecord。当然ActivityRecord中也存放了自己所属的TaskRecord,TaskRecord中也有ActivityStack的引用。我们可以用dumpsys activity activities命令来查看当前系统中的activity,task,和stack的关系,输出如下:
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
Stack #1:
Task id #15
* TaskRecord{2e8becf3 #15 A=com.example.weiwn.myapplication U=0 sz=1}
userId=0 effectiveUid=u0a22131 mCallingUid=u0a22072 mCallingPackage=com.amazon.firelauncher
affinity=com.example.weiwn.myapplication
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.weiwn.myapplication/.FragmentTestActivity}
realActivity=com.example.weiwn.myapplication/.FragmentTestActivity
autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=0 mTaskToReturnTo=1
rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false
Activities=[ActivityRecord{17b76956 u0 com.example.weiwn.myapplication/.FragmentTestActivity t15}]
askedCompatMode=false inRecents=true isAvailable=true
lastThumbnail=android.graphics.Bitmap@2ab81db0 lastThumbnailFile=/data/system/recent_images/15_task_thumbnail.png
hasBeenVisible=true firstActiveTime=1494236891742 lastActiveTime=1494236891742 (inactive for 14s)
* Hist #0: ActivityRecord{17b76956 u0 com.example.weiwn.myapplication/.FragmentTestActivity t15}
packageName=com.example.weiwn.myapplication processName=com.example.weiwn.myapplication
launchedFromUid=32072 launchedFromPackage=com.amazon.firelauncher userId=0
app=ProcessRecord{2f907429 26495:com.example.weiwn.myapplication/u0a22131}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.weiwn.myapplication/.FragmentTestActivity }
frontOfTask=true task=TaskRecord{2e8becf3 #15 A=com.example.weiwn.myapplication U=0 sz=1}
taskAffinity=com.example.weiwn.myapplication
realActivity=com.example.weiwn.myapplication/.FragmentTestActivity
baseDir=/system/priv-app/com.example.weiwn.myapplication/com.example.weiwn.myapplication.apk
dataDir=/data/data/com.example.weiwn.myapplication
stateNotNeeded=false componentSpecified=true mActivityType=0
compat={240dpi always-compat} labelRes=0x7f060014 icon=0x7f030000 theme=0x7f09008a
config={1.0 ?mcc?mnc en_US ?layoutDir sw800dp w800dp h1208dp 240dpi xlrg port finger -keyb/v/h -nav/h s.5}
taskDescription: iconFilename=null label="null" color=ffe6e6e6
launchFailed=false launchCount=0 lastLaunchTime=-1h38m9s379ms
haveState=false icicle=null
state=RESUMED stopped=false delayedResume=false finishing=false
keysPaused=false inHistory=true visible=true sleeping=false idle=true
fullscreen=true noDisplay=false immersive=false launchMode=0
frozenBeforeDestroy=false forceNewConfig=false
mActivityType=APPLICATION_ACTIVITY_TYPE
displayStartTime=-14s486ms startTime=0
waitingVisible=false nowVisible=true lastVisibleTime=-1h38m9s140ms
Running activities (most recent first):
TaskRecord{2e8becf3 #15 A=com.example.weiwn.myapplication U=0 sz=1}
Run #0: ActivityRecord{17b76956 u0 com.example.weiwn.myapplication/.FragmentTestActivity t15}
mResumedActivity: ActivityRecord{17b76956 u0 com.example.weiwn.myapplication/.FragmentTestActivity t15}
mLastPausedActivity: ActivityRecord{17b76956 u0 com.example.weiwn.myapplication/.FragmentTestActivity t15}
ActivityStack主要用于关于回退栈的逻辑,它调度的单元是TaskRecord。而TaskRecord管理ActivityRecord的先后顺序,对自己管理的ActivityRecord进行排序,创建,销毁。
当从home(launcher)启动一个activity时,launcher会向AMS发送一个intent。这个intent大概是这样的结构。
Intent{act=main,cagetary=launcher,cmp=packageName/activityName, flg=0x10000000}
例如:Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]
flg=0x10000000 cmp=com.example.weiwn.myapplication/.FragmentTestActivity }
AMS收到这个intent后,会创建一个ActivityRecord,然后把这个ActivityRecord放到对应的TaskRecord中(和ActivityRecord的taskAffinity相同的TaskRecord),并把该TaskRecord移动到前台。
1. 如果系统之前不存在和ActivityRecord对应的TaskRecord(可能是该activity之前没有被启动过,也可能是该activity所在的app中的其他activity已经启动了,也创建了对应的TaskRecord,但是该activity和已经启动的activity的taskAffinity不同),则AMS会创建对应TaskRecord,并把新的ActivityRecord放入该TaskRecord。
2. 如果AMS发现了对应的TaskRecord,则要根据activity的launchMode和TaskRecord的baseIntent,TopActivity来决定是否将新的ActivityRecord加入该TaskRecord,还是复用TaskRecord中原有的activity。
final int
startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
boolean doResume, Bundle options, TaskRecord inTask) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
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;
intent.setFlags(launchFlags);
// We may want to try to place the new activity in to an existing task. We always
// do this if the target activity is singleTask or singleInstance; we will also do
// this if NEW_TASK has been requested, and there is not an additional qualifier telling
// us to still place it in a new task: multi task, always doc mode, or being asked to
// launch this as a new task behind the current one.
if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| launchSingleInstance||launchSingleTask) { 多task模式
// If bring to front is requested, and no result is requested and we have not
// been given an explicit task to launch in to, and
// we can find a task that was started with this same
// component, then instead of launching bring that one to the front.
if (inTask == null && r.resultTo == null) { 用户没有指定用某个特定的task来存放activity
// 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= !launchSingleInstance ?
findTaskLocked(r) : findActivityLocked(intent, r.info); 寻找能匹配要启动的activity的task,匹配的原则是目标activity的intent和task的intent相似,相似包括taskAffinity相同,intent的component相同,以及affinityIntent的component相同,如果找到了,则返回该task的topActivity
if (intentActivity != null) {
if (r.task == null) {
r.task = intentActivity.task;
}
targetStack = intentActivity.task.stack;
targetStack.mLastPausedActivity = null;
if (DEBUG_TASKS) Slog.d(TAG, "Bring to front target: " + targetStack
+ " from " + intentActivity);
targetStack.moveToFront();
if (intentActivity.task.intent == null) {
// This task was started because of movement of
// the activity based on affinity... now that we
// are actually launching it, we can assign the
// base intent.
intentActivity.task.setIntent(r);
}
// If the caller has requested that the target task be
// reset, then do so.
if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
}
if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and
// the client said not to do anything if that
// is the case, so this is it! And for paranoia, make
// sure we have correctly resumed the top activity.
if (doResume) {
resumeTopActivitiesLocked(targetStack, null, options);
} else {
ActivityOptions.abort(options);
}
return ActivityManager.START_RETURN_INTENT_TO_CALLER;
}
if ((launchFlags &
(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
== (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
// The caller has requested to completely replace any
// existing task with its new activity. Well that should
// not be too hard...
清理已有的task
reuseTask = intentActivity.task;
reuseTask.performClearTaskLocked();
reuseTask.setIntent(r);
} else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
|| launchSingleInstance || launchSingleTask) {
// In this situation we want to remove all activities
// from the task up to the one being started. In most
// cases this means we are resetting the task to its
// initial state.
如果在task中找到了要启动的activity,则清理该activity之上的activity
ActivityRecord top =
intentActivity.task.performClearTaskLocked(r, launchFlags);
if (top != null) {
说明找到了相同的activity
if (top.frontOfTask) {
// Activity aliases may mean we use different
// intents for the top activity, so make sure
// the task now has the identity of the new
// intent.
top.task.setIntent(r);
}
ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT,
r, top.task);
top.deliverNewIntentLocked(callingUid, r.intent);
} else {
没找到对应的activity,需要启动一个新的
// A special case: we need to
// start the activity because it is not currently
// running, and the caller has asked to clear the
// current task to have this activity at the top.
addingToTask = true;
// Now pretend like this activity is being started
// by the top of its task, so it is put in the
// right place.
sourceRecord = intentActivity;
}
} else if (r.realActivity.equals(intentActivity.task.realActivity)) { 如果要启动的activity和匹配task的topActivity的className相同
// In this case the top activity on the task is the
// same as the one being launched, so we take that
// as a request to bring the task to the foreground.
// If the top activity in the task is the root
// activity, deliver this new intent to it if it
// desires.
if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 || launchSingleTop)
&& intentActivity.realActivity.equals(r.realActivity)) {
if (intentActivity.frontOfTask) {
intentActivity.task.setIntent(r);
}
intentActivity.deliverNewIntentLocked(callingUid, r.intent);
} else if (!r.intent.filterEquals(intentActivity.task.intent)) {
// In this case we are launching the root activity
// of the task, but with a different intent. We
// should start a new instance on top.
如果要启动的activity和task中的topActivity是同一个activity,但是启动activity的intent和task的baseintent不同,则需要启动一个新的activity实例,放在task的top
addingToTask = true;
sourceRecord = intentActivity;
}
} else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
// In this case an activity is being launched in to an
// existing task, without resetting that task. This
// is typically the situation of launching an activity
// from a notification or shortcut. We want to place
// the new activity on top of the current task.
正常情况下,要启动的activity和task的topActivity不同,而且也不需要reset task,这时只是把新的activity加入task
addingToTask = true;
sourceRecord = intentActivity;
} else if (!intentActivity.task.rootWasReset) {
// In this case we are launching in to an existing task
// that has not yet been started from its front door.
// The current task has been brought to the front.
// Ideally, we'd probably like to place this new task
// at the bottom of its stack, but that's a little hard
// to do with the current organization of the code so
// for now we'll just drop it.
intentActivity.task.setIntent(r);
}
if (!addingToTask && reuseTask == null) { 如果不需要往现有的task中加入activity,则把现有的task移动到前台,然后返回
// We didn't do anything... but it was needed (a.k.a., client
// don't use that intent!) And for paranoia, make
// sure we have correctly resumed the top activity.
if (doResume) {
targetStack.resumeTopActivityLocked(null, options);
} else {
ActivityOptions.abort(options);
}
return ActivityManager.START_TASK_TO_FRONT;
}
}
}
}
}
从上面的代码逻辑可以看到,想要一个task包含多个相同的activity,不仅要求activity是多实例模式(standard,singleTop),而且还要求新的activity的启动intent和task的base intent不同才行。
实际操作
以上面例子中的com.example.weiwn.myapplication/.FragmentTestActivity说明,它是从launcher启动的,所以启动的intent为
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.weiwn.myapplication/.FragmentTestActivity}
从dumpsys activity activities命令的输出看,系统创建了一个task,即
TaskRecord{2e8becf3
#15 A=com.example.weiwn.myapplication U=0 sz=1}
userId=0 effectiveUid=u0a22131 mCallingUid=u0a22072 mCallingPackage=com.amazon.firelauncher
affinity=com.example.weiwn.myapplication
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.weiwn.myapplication/.FragmentTestActivity}
这个task的base intent就是启动activity的intent(因为该activity是该task的root activity),即
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.weiwn.myapplication/.FragmentTestActivity}
这时如果运行 “adb shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n com.example.weiwn.myapplication/.FragmentTestActivity” 命令,则AMS 不会新建一个ActivityRecord,因为命令中的intent和TaskRecord的intent相同。
输出如下:
Starting: Intent { act=android.intent.action.MAIN
cat=[android.intent.category.LAUNCHER] cmp=com.example.weiwn.myapplication/.FragmentTestActivity }
Warning: Activity not started, its current task has been brought to the front
这表明AMS在已经启动的task中找到了和命令中intent对应的task,并把它移动到前台。
同时回调Activity的callback。 是否回调onNewIntent取决于activity的launch
mode, 非standard时会回调onNewIntent。
E/FragmentTestActivity(24074):
onNewIntent---begin----com.example.weiwn.myapplication.FragmentTestActivity@1fa7c7c3
E/FragmentTestActivity(24074): onNewIntent---end
E/FragmentTestActivity(24074): onRestart---begin----com.example.weiwn.myapplication.FragmentTestActivity@1fa7c7c3
E/FragmentTestActivity(24074): onRestart---end
E/FragmentTestActivity(24074): onStart---begin-----com.example.weiwn.myapplication.FragmentTestActivity@1fa7c7c3
E/FragmentTestActivity(24074): onStart---end
E/FragmentTestActivity(24074): onResume---begin----com.example.weiwn.myapplication.FragmentTestActivity@1fa7c7c3
E/FragmentTestActivity(24074): onResume---end
E/FragmentTestActivity(24074): onPostResume---begin----com.example.weiwn.myapplication.FragmentTestActivity@1fa7c7c3
E/FragmentTestActivity(24074): onPostResume---end
如果想在原有的task中在启动一个相同的activity(前提是activity的launch mode必须是standard),只需要传入不同于task的intent即可。
如:adb
shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
-p com.example.weiwn.myapplication -n com.example.weiwn.myapplication/.FragmentTestActivity
输出如下:
Starting: Intent { act=android.intent.action.MAIN
cat=[android.intent.category.LAUNCHER] pkg=com.example.weiwn.myapplication cmp=com.example.weiwn.myapplication/.FragmentTestActivity }
Dumpsys activity activities的结果如下:
ACTIVITY MANAGER ACTIVITIES
(dumpsys activity activities)
Display #0 (activities from top to bottom):
Stack #1:
Task id #36
* TaskRecord{f9818c6 #36 A=com.example.weiwn.myapplication U=0 sz=2}
userId=0 effectiveUid=u0a37 mCallingUid=0 mCallingPackage=null
affinity=com.example.weiwn.myapplication
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.weiwn.myapplication/.FragmentTestActivity}
realActivity=com.example.weiwn.myapplication/.FragmentTestActivity
autoRemoveRecents=false isPersistable=true numFullscreen=2 taskType=0 mTaskToReturnTo=1
rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false
Activities=[ActivityRecord{145e62ba u0 com.example.weiwn.myapplication/.FragmentTestActivity t36}, ActivityRecord{19df37d7 u0 com.example.weiwn.myapplication/.FragmentTestActivity t36}]
askedCompatMode=false inRecents=true isAvailable=true
lastThumbnail=android.graphics.Bitmap@2a731087 lastThumbnailFile=/data/system/recent_images/36_task_thumbnail.png
hasBeenVisible=true firstActiveTime=1494590163822 lastActiveTime=1494590163822 (inactive for 96s)
* Hist #1: ActivityRecord{19df37d7 u0 com.example.weiwn.myapplication/.FragmentTestActivity t36}
packageName=com.example.weiwn.myapplication processName=com.example.weiwn.myapplication
launchedFromUid=0 launchedFromPackage=null userId=0
app=ProcessRecord{31715bb4 6647:com.example.weiwn.myapplication/u0a37}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10400000 pkg=com.example.weiwn.myapplication cmp=com.example.weiwn.myapplication/.FragmentTestActivity }
frontOfTask=false task=TaskRecord{f9818c6 #36 A=com.example.weiwn.myapplication U=0 sz=2}
taskAffinity=com.example.weiwn.myapplication
realActivity=com.example.weiwn.myapplication/.FragmentTestActivity
baseDir=/data/app/com.example.weiwn.myapplication-2/base.apk
dataDir=/data/data/com.example.weiwn.myapplication
stateNotNeeded=false componentSpecified=true mActivityType=0
compat={240dpi always-compat} labelRes=0x7f060014 icon=0x7f030000 theme=0x7f09008a
config={1.0 ?mcc?mnc en_US ?layoutDir sw800dp w1280dp h728dp 240dpi xlrg land finger -keyb/v/h -nav/h s.6}
taskDescription: iconFilename=null label="null" color=ffe6e6e6
launchFailed=false launchCount=1 lastLaunchTime=-1m36s265ms
haveState=false icicle=null
state=RESUMED stopped=false delayedResume=false finishing=false
keysPaused=false inHistory=true visible=true sleeping=false idle=true
fullscreen=true noDisplay=false immersive=false launchMode=0
frozenBeforeDestroy=false forceNewConfig=false
mActivityType=APPLICATION_ACTIVITY_TYPE
waitingVisible=false nowVisible=true lastVisibleTime=-1m35s917ms
* Hist #0: ActivityRecord{145e62ba u0 com.example.weiwn.myapplication/.FragmentTestActivity t36}
packageName=com.example.weiwn.myapplication processName=com.example.weiwn.myapplication
launchedFromUid=0 launchedFromPackage=null userId=0
app=ProcessRecord{31715bb4 6647:com.example.weiwn.myapplication/u0a37}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.weiwn.myapplication/.FragmentTestActivity }
frontOfTask=true task=TaskRecord{f9818c6 #36 A=com.example.weiwn.myapplication U=0 sz=2}
taskAffinity=com.example.weiwn.myapplication
realActivity=com.example.weiwn.myapplication/.FragmentTestActivity
baseDir=/data/app/com.example.weiwn.myapplication-2/base.apk
dataDir=/data/data/com.example.weiwn.myapplication
stateNotNeeded=false componentSpecified=true mActivityType=0
compat={240dpi always-compat} labelRes=0x7f060014 icon=0x7f030000 theme=0x7f09008a
config={1.0 ?mcc?mnc en_US ?layoutDir sw800dp w1280dp h728dp 240dpi xlrg land finger -keyb/v/h -nav/h s.6}
taskDescription: iconFilename=null label="null" color=ffe6e6e6
launchFailed=false launchCount=0 lastLaunchTime=-2m27s232ms
haveState=true icicle=Bundle[mParcelledData.dataSize=1524]
state=STOPPED stopped=true delayedResume=false finishing=false
keysPaused=false inHistory=true visible=false sleeping=false idle=true
fullscreen=true noDisplay=false immersive=false launchMode=0
frozenBeforeDestroy=false forceNewConfig=false
mActivityType=APPLICATION_ACTIVITY_TYPE
waitingVisible=false nowVisible=false lastVisibleTime=-2m4s623ms
Running activities (most recent first):
TaskRecord{f9818c6 #36 A=com.example.weiwn.myapplication U=0 sz=2}
Run #1: ActivityRecord{19df37d7 u0 com.example.weiwn.myapplication/.FragmentTestActivity t36}
Run #0: ActivityRecord{145e62ba u0 com.example.weiwn.myapplication/.FragmentTestActivity t36}
mResumedActivity: ActivityRecord{19df37d7 u0 com.example.weiwn.myapplication/.FragmentTestActivity t36}
这篇博客探讨了在Android中如何使用AMS(Activity Manager Service)管理Activity。通过`dumpsys activity activities`命令,展示了当启动一个Activity时,如何判断是否在现有Task中创建新记录或复用旧记录。内容详细解释了ActivityStack、TaskRecord和ActivityRecord之间的关系,以及如何根据launchMode和任务的baseIntent来决定Activity的放置策略。示例中,同一个Activity在Task中有多个记录的情况,并通过命令行操作验证了Activity的启动行为。

1911

被折叠的 条评论
为什么被折叠?



