同一个Activity在Task中存在多个记录

这篇博客探讨了在Android中如何使用AMS(Activity Manager Service)管理Activity。通过`dumpsys activity activities`命令,展示了当启动一个Activity时,如何判断是否在现有Task中创建新记录或复用旧记录。内容详细解释了ActivityStack、TaskRecord和ActivityRecord之间的关系,以及如何根据launchMode和任务的baseIntent来决定Activity的放置策略。示例中,同一个Activity在Task中有多个记录的情况,并通过命令行操作验证了Activity的启动行为。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

在 android 中,AMS用于管理activity的创建,启动,和销毁。

AMS用ActivityStackTaskRecord,和ActivityRecord三个class来管理所有的activity。


三者的关系是ActivityStack中存放所有的TaskRecordTaskRecord中存放属于自己的ActivityRecord。当然ActivityRecord中也存放了自己所属的TaskRecordTaskRecord中也有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的launchModeTaskRecordbaseIntent,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}



开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值