“在整理完启动模式后,我发现大家对启动模式的理解是有误区的“

引言

再谈启动模式,貌似没啥意思。但是你能正确回答下面的问题吗?

  • 问题1:singleTask启动模式,在启动新的Activity的时候,真的会重新创建新的任务栈吗,所谓的任务栈存在指的是什么?
  • 问题2:设置了Intent.FLAG_ACTIVITY_NEW_TASK,每次都会创建新的任务栈吗?
  • 问题3:对于singleInstance,显式指定taskAffinity为应用包名,那在启动的时候,还会创建新的任务栈吗?

以下实验基于Android 9.0。如果大家只是想了解每种启动模式的基本概念,可以只看对应的任务栈图,直接忽略掉每一小点。

笔者会用adb的打印,看每一种启动模式下,任务栈的变化。

一、ActivityRecord、TaskRecord、ActivityStack的区别

在回答上面的问题前,我们先整理下ActivityRecord、TaskRecord、ActivityStack的区别。

  • ActivityRecord:一个ActivityRecord对应一个Activity,保存了Activity的所有信息;但一个Activity可能会有多个ActivityRecord,因为Activity可以被多次启动,这个主要取决于其启动模式。

  • TaskRecord:一个TaskRecord由一个或者多个ActivityRecord组成,这就是我们常说的任务栈,具有后进先出的特点。

  • ActivityStack:ActivityStack是用来管理TaskRecord的,包含了多个TaskRecord。

这里简单贴一下代码,让读者对这些概念有一些了解

/*** State and management of a single stack of activities.*/
class ActivityStack extends ConfigurationContainer {/*** The back history of all previous (and possibly still* running) activities.  It contains #TaskRecord objects.*/private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();//..... 省略代码
}class TaskRecord extends ConfigurationContainer {/** *List of all activities in the task arranged in history order */final ArrayList<ActivityRecord> mActivities;//..... 省略代码
}/*** An entry in the history stack, representing an activity.*/
final class ActivityRecord extends ConfigurationContainer {final ActivityInfo info; // all about meint launchMode;         // the launch mode activity attribute.final String launchedFromPackage; // always the package who started the activity.//..... 省略代码
}

启动模式的实际值

如果大家看了上面的代码,就会发现launchMode的值是int类型,与我们认知的不太一样,这里贴一下映射关系。

public class ActivityInfo extends ComponentInfo implements Parcelable {/** * Constant corresponding to <code>standard</code> in * the {@link android.R.attr#launchMode} attribute.*/public static final int LAUNCH_MULTIPLE = 0;/** * Constant corresponding to <code>singleTop</code> in * the {@link android.R.attr#launchMode} attribute. */public static final int LAUNCH_SINGLE_TOP = 1;/** * Constant corresponding to <code>singleTask</code> in * the {@link android.R.attr#launchMode} attribute. */public static final int LAUNCH_SINGLE_TASK = 2;/** * Constant corresponding to <code>singleInstance</code> in * the {@link android.R.attr#launchMode} attribute. */public static final int LAUNCH_SINGLE_INSTANCE = 3;public String taskAffinity;}

我们注意到ActivityInfo里面有一个taskAffinity字段,想必大家应该知道其作用了——指定任务栈名。不知道也没关系,后面会结合启动模式讲解。

使用dumpsys 命令查看任务栈

我们先来看看在launcher下,任务栈的情况

在cmd下运行 adb shell dumpsys activity a可以得到如下信息

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):Stack #0: type=home mode=fullscreenisSleeping=falsemBounds=Rect(0, 0 - 0, 0)Task id #1mBounds=Rect(0, 0 - 0, 0)mMinWidth=-1mMinHeight=-1mLastNonFullscreenBounds=null* TaskRecord{a9becdf #1 I=com.miui.home/.launcher.Launcher U=0 StackId=0 sz=1}userId=0 effectiveUid=u0a23 mCallingUid=1000 mUserSetupComplete=true mCallingPackage=com.android.systemuiintent={act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10800100 cmp=com.miui.home/.launcher.Launcher}realActivity=com.miui.home/.launcher.LauncherautoRemoveRecents=false isPersistable=false numFullscreen=1 activityType=2rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLEActivities=[ActivityRecord{3705f28 u0 com.miui.home/.launcher.Launcher t1}]askedCompatMode=false inRecents=true isAvailable=truestackId=0* Hist #0: ActivityRecord{3705f28 u0 com.miui.home/.launcher.Launcher t1}packageName=com.miui.home processName=com.miui.homelaunchedFromUid=0 launchedFromPackage=null userId=0app=ProcessRecord{2a0244b 2458:com.miui.home/u0a23}Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10800100 cmp=com.miui.home/.launcher.Launcher }frontOfTask=true task=TaskRecord{a9becdf #1 I=com.miui.home/.launcher.Launcher U=0 StackId=0 sz=1}taskAffinity=nullfullscreen=true noDisplay=false immersive=false launchMode=2
  • Stack #0:即home下的ActivityStack id为0。
  • Task id #1:即ActivityStack中有一个id为1的TaskRecord。
  • Activities:用来存放ActivityRecord的,任务栈存放Activity的位置,可以通过这个关键字查看Activity的入栈出栈情况。“com.miui.home/.launcher.Launcher” 就是对应Activity的类路径。
  • taskAffinity:细心的读者应该会注意到这个字段,就是我们在manifest文件中配置的,这里为null表明没有添加taskAffinity。
  • launchMode:即启动模式,对应值为int。

二、Standard模式

默认值。系统在启动该 Activity 的任务中创建 Activity 的新实例,并将 intent 传送给该实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例。

比如 A启动B,B启动A的任务栈变化情况如下


新建一个项目,MainActivity启动 SecondActivity,都是标准的启动模式。对应的Activity配置如下

        <activityandroid:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activityandroid:name=".StandardActivity"/>

1)intent不设置flag,不配置taskAffinity

新建一个项目,MainActivity启动 SecondActivity,都是标准的启动模式。打印的任务栈如下:

  Stack #9: type=standard mode=fullscreenisSleeping=falsemBounds=Rect(0, 0 - 0, 0)Task id #18359mBounds=Rect(0, 0 - 0, 0)mMinWidth=-1mMinHeight=-1mLastNonFullscreenBounds=null* TaskRecord{b751d5b #18359 A=com.example.launchmode U=0 StackId=31 sz=2}affinity=com.example.launchmode....Activities=[ActivityRecord{8b23942 u0 com.example.launchmode/.MainActivity t18359}, ActivityRecord{d0a45e5 u0 com.example.launchmode/.StandardActivity t18359}]....

可以看到Activities 里面有两个ActivityRecord,分别对应MainActivity,SecondActivity。这里贴一下对于的生命周期(注意,面试必考题)和TaskId,可以看到Task Id是一样的:

04-18 10:53:37.981 15379 15379 D MainActivity: onCreate:  Task id 18359
04-18 10:53:38.029 15379 15379 D MainActivity: onStart:  Task id 18359
04-18 10:53:38.032 15379 15379 D MainActivity: onResume:  Task id 18359
04-18 10:53:39.611 15379 15379 D MainActivity: ------------- start activity -------------
04-18 10:53:39.622 15379 15379 D MainActivity: onPause:  Task id 18359
04-18 10:53:39.635 15379 15379 D StandardActivity: onCreate:  Task id 18359
04-18 10:53:39.649 15379 15379 D StandardActivity: onStart:  Task id 18359
04-18 10:53:39.651 15379 15379 D StandardActivity: onResume:  Task id 18359
04-18 10:53:40.199 15379 15379 D MainActivity: onStop:  Task id 18359

Activity里面可以通过 getTaskId() 方式获取到TaskId,具体实现就不再讲解,作为一个课后作业。

2)只设置intent为FLAG_ACTIVITY_NEW_TASK

在MainActivity启动StandardActivity的时候,设置flag如下

    public void next(View view) {Log.d(TAG, "------------- start activity -------------");Intent intent = new Intent(this, StandardActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);}

对应的生命周期和TaskId如下,可见并没有启动新的任务栈

04-18 10:57:12.584 15563 15563 D MainActivity: onCreate:  Task id 18361
04-18 10:57:12.631 15563 15563 D MainActivity: onStart:  Task id 18361
04-18 10:57:12.634 15563 15563 D MainActivity: onResume:  Task id 18361
04-18 10:57:14.096 15563 15563 D MainActivity: ------------- start activity -------------
04-18 10:57:14.105 15563 15563 D MainActivity: onPause:  Task id 18361
04-18 10:57:14.117 15563 15563 D StandardActivity: onCreate:  Task id 18361
04-18 10:57:14.132 15563 15563 D StandardActivity: onStart:  Task id 18361
04-18 10:57:14.134 15563 15563 D StandardActivity: onResume:  Task id 18361
04-18 10:57:14.677 15563 15563 D MainActivity: onStop:  Task id 18361

3)只配置taskAffinity

在manifest中配置StandardActivity的taskAffinity为 com.demo.launchmode

        <activityandroid:name=".StandardActivity"android:taskAffinity="com.demo.launchmode"/>

直接启动standardActivity,对应的周期和TaskId如下,可见这种方式也不会启动新的任务栈。

04-18 11:02:51.802 16954 16954 D MainActivity: onCreate:  Task id 18363
04-18 11:02:51.849 16954 16954 D MainActivity: onStart:  Task id 18363
04-18 11:02:51.852 16954 16954 D MainActivity: onResume:  Task id 18363
04-18 11:02:53.283 16954 16954 D MainActivity: ------------- start activity -------------
04-18 11:02:53.293 16954 16954 D MainActivity: onPause:  Task id 18363
04-18 11:02:53.306 16954 16954 D StandardActivity: onCreate:  Task id 18363
04-18 11:02:53.321 16954 16954 D StandardActivity: onStart:  Task id 18363
04-18 11:02:53.323 16954 16954 D StandardActivity: onResume:  Task id 18363
04-18 11:02:53.873 16954 16954 D MainActivity: onStop:  Task id 18363

4)intent设置FLAG_ACTIVITY_NEW_TASK 、配置taskAffinity

按照2)、3)方式,同时在intent设置FLAG_ACTIVITY_NEW_TASK 和配置taskAffinity。MainActivity与standardActivity的TaskId就不一样了。对应的栈结构,可以通过上面的dumpsys命令查看,这里就不列出来了。

04-18 11:04:55.107 17185 17185 D MainActivity: onCreate:  Task id 18365
04-18 11:04:55.154 17185 17185 D MainActivity: onStart:  Task id 18365
04-18 11:04:55.156 17185 17185 D MainActivity: onResume:  Task id 18365
04-18 11:04:56.707 17185 17185 D MainActivity: ------------- start activity -------------
04-18 11:04:56.721 17185 17185 D MainActivity: onPause:  Task id 18365
04-18 11:04:56.751 17185 17185 D StandardActivity: onCreate:  Task id 18366
04-18 11:04:56.774 17185 17185 D StandardActivity: onStart:  Task id 18366
04-18 11:04:56.776 17185 17185 D StandardActivity: onResume:  Task id 18366
04-18 11:04:57.560 17185 17185 D MainActivity: onStop:  Task id 18365

画重点:要同时指定taskAffinity和FLAG_ACTIVITY_NEW_TASK才能在新的任务栈启动Standard的Activity

三、SingleTop模式

如果当前任务的顶部已存在 Activity 的实例,则系统会通过调用其 onNewIntent() 方法来将 intent 转送给该实例,而不是创建 Activity 的新实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例(但前提是返回堆栈顶部的 Activity 不是该 Activity 的现有实例)。

如果A以Standard启动B,B再以SingleTop启动B,对应的图为:

如果A以Standard启动B,B再以SingleTop启动A,对应的图为:

对应的Activity配置如下

   <activityandroid:name=".SingleTopActivity"android:launchMode="singleTop" />

1)intent不设置flag,不配置taskAffinity

​ 对应的周期是,这种情况是不会创建新的任务栈

04-18 16:40:43.267 24395 24395 D MainActivity: onCreate:  Task id 18428
04-18 16:40:43.315 24395 24395 D MainActivity: onStart:  Task id 18428
04-18 16:40:43.318 24395 24395 D MainActivity: onResume:  Task id 18428
04-18 16:40:59.145 24395 24395 D MainActivity: ------------- start activity -------------
04-18 16:40:59.168 24395 24395 D MainActivity: onPause:  Task id 18428
04-18 16:40:59.180 24395 24395 D SingleTopActivity: onCreate:  Task id 18428
04-18 16:40:59.196 24395 24395 D SingleTopActivity: onStart:  Task id 18428
04-18 16:40:59.198 24395 24395 D SingleTopActivity: onResume:  Task id 18428
04-18 16:40:59.747 24395 24395 D MainActivity: onStop:  Task id 18428

2)只设置intent为FLAG_ACTIVITY_NEW_TASK

对应的周期和TaskId如下,可见这种方式不会启动新的任务栈。

04-18 10:28:48.458 14065 14065 D MainActivity: onCreate:  Task id 18343
04-18 10:28:48.505 14065 14065 D MainActivity: onStart:  Task id 18343
04-18 10:28:48.508 14065 14065 D MainActivity: onResume:  Task id 18343
04-18 10:28:50.358 14065 14065 D MainActivity: ------------- start activity -------------
04-18 10:28:50.367 14065 14065 D MainActivity: onPause:  Task id 18343
04-18 10:28:50.380 14065 14065 D SingleTopActivity: onCreate:  Task id 18343
04-18 10:28:50.394 14065 14065 D SingleTopActivity: onStart:  Task id 18343
04-18 10:28:50.396 14065 14065 D SingleTopActivity: onResume:  Task id 18343
04-18 10:28:50.939 14065 14065 D MainActivity: onStop:  Task id 18343

3)只配置taskAffinity

对应的周期和TaskId如下,可见这种方式也不会启动新的任务栈。

04-18 10:35:29.907 14560 14560 D MainActivity: onCreate:  Task id 18349
04-18 10:35:29.954 14560 14560 D MainActivity: onStart:  Task id 18349
04-18 10:35:29.957 14560 14560 D MainActivity: onResume:  Task id 18349
04-18 10:35:32.096 14560 14560 D MainActivity: ------------- start activity -------------
04-18 10:35:32.107 14560 14560 D MainActivity: onPause:  Task id 18349
04-18 10:35:32.118 14560 14560 D SingleTopActivity: onCreate:  Task id 18349
04-18 10:35:32.132 14560 14560 D SingleTopActivity: onStart:  Task id 18349
04-18 10:35:32.134 14560 14560 D SingleTopActivity: onResume:  Task id 18349
04-18 10:35:32.699 14560 14560 D MainActivity: onStop:  Task id 18349

4)intent设置FLAG_ACTIVITY_NEW_TASK 、配置taskAffinity

周期、taskId如下,可以看到,singleTop的taskid已经变了。即这种方式可以启动新的任务栈。

04-18 10:31:35.382 14357 14357 D MainActivity: onCreate:  Task id 18345
04-18 10:31:35.430 14357 14357 D MainActivity: onStart:  Task id 18345
04-18 10:31:35.432 14357 14357 D MainActivity: onResume:  Task id 18345
04-18 10:31:37.143 14357 14357 D MainActivity: ------------- start activity -------------
04-18 10:31:37.158 14357 14357 D MainActivity: onPause:  Task id 18345
04-18 10:31:37.184 14357 14357 D SingleTopActivity: onCreate:  Task id 18346
04-18 10:31:37.199 14357 14357 D SingleTopActivity: onStart:  Task id 18346
04-18 10:31:37.201 14357 14357 D SingleTopActivity: onResume:  Task id 18346
04-18 10:31:38.004 14357 14357 D MainActivity: onStop:  Task id 18345

5)栈顶情况

在栈顶的时候,singletop的周期如下。可以看到会先调用onPause,然后才调用onNewIntent,onResume也会重新调用。

04-18 10:21:18.128 13663 13663 D SingleTopActivity: onCreate:  Task id 18338
04-18 10:21:18.143 13663 13663 D SingleTopActivity: onStart:  Task id 18338
04-18 10:21:18.145 13663 13663 D SingleTopActivity: onResume:  Task id 18338
04-18 10:21:21.629 13663 13663 D SingleTopActivity:  -------------- start singleTop activity --------------
04-18 10:21:21.651 13663 13663 D SingleTopActivity: onPause:  Task id 18338
04-18 10:21:21.653 13663 13663 D SingleTopActivity: onNewIntent:  Task id 18338
04-18 10:21:21.655 13663 13663 D SingleTopActivity: onResume:  Task id 18338

画重点:onNewIntent 方法中传入的intent是新启动Activity的intent,与getIntent() 方法返回的intent不是同一个intent

6) 非栈顶情况

在介绍singleTop的时候,有提到一个任务栈里面可以有多个这种模式的实例,我们来验证一下

配置文件中设置singleTopActivity为主入口,singleTopActivity启动MainActivity,MainActivity再启动singleTopActivity。

       <activityandroid:name=".MainActivity"android:launchMode="standard"></activity><activityandroid:name=".SingleTopActivity"android:launchMode="singleTop"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>

对应的任务栈如下,我们可以看到id为 18351的任务栈中有两个SingleTopActivity的实例(不是同一个实例)。

Task id #18351
* TaskRecord{f846b29 #18351 A=com.example.launchmode U=0 StackId=23 sz=3}Activities=[ActivityRecord{7e1765c u0 com.example.launchmode/.SingleTopActivity t18351}, ActivityRecord{95d526f u0 com.example.launchmode/.MainActivity t18351}, ActivityRecord{76569fe u0 com.example.launchmode/.SingleTopActivity t18351}]

画重点:singleTop只进行栈顶的复用,如果不在栈顶,就会创建新的实例

四、singleTask模式

系统会创建新任务,并实例化新任务的根 Activity。但是,如果另外的任务中已存在该 Activity 的实例,则系统会通过调用其 onNewIntent() 方法将 intent 转送到该现有实例,而不是创建新实例。Activity 一次只能有一个实例存在。

如果A以Standard方式启动B,B再以SingleTask启动A,对应的图为:

这种启动模式会比较复杂,先验证什么时候会启动新的任务栈。对应的Activity配置如下

    <activityandroid:name=".SingleTaskActivity"android:launchMode="singleTask" />

1)intent不设置flag,不配置taskAffinity

周期和任务栈如下。恩?SingleTaskActivity与MainActivity的TaskId一样?是的,由于我们没有给SingleTaskActivity配置taskAffinity,所以SingleTaskActivity会复用默认的任务栈。

04-18 11:18:33.266 17490 17490 D MainActivity: onCreate:  Task id 18368
04-18 11:18:33.313 17490 17490 D MainActivity: onStart:  Task id 18368
04-18 11:18:33.316 17490 17490 D MainActivity: onResume:  Task id 18368
04-18 11:18:35.122 17490 17490 D MainActivity: ------------- start activity -------------
04-18 11:18:35.132 17490 17490 D MainActivity: onPause:  Task id 18368
04-18 11:18:35.146 17490 17490 D SingleTaskActivity: onCreate:  Task id 18368
04-18 11:18:35.160 17490 17490 D SingleTaskActivity: onStart:  Task id 18368
04-18 11:18:35.162 17490 17490 D SingleTaskActivity: onResume:  Task id 18368
04-18 11:18:35.712 17490 17490 D MainActivity: onStop:  Task id 18368

2)只设置intent为FLAG_ACTIVITY_NEW_TASK

设置NEW_TASK总可以创建新的任务栈吧?然鹅,周期和任务栈如下。啊哈?也没有启动新的任务栈?是不是有点超出认知范围了?

04-18 11:22:33.415 17817 17817 D MainActivity: onCreate:  Task id 18371
04-18 11:22:33.462 17817 17817 D MainActivity: onStart:  Task id 18371
04-18 11:22:33.465 17817 17817 D MainActivity: onResume:  Task id 18371
04-18 11:22:34.887 17817 17817 D MainActivity: ------------- start activity -------------
04-18 11:22:34.897 17817 17817 D MainActivity: onPause:  Task id 18371
04-18 11:22:34.912 17817 17817 D SingleTaskActivity: onCreate:  Task id 18371
04-18 11:22:34.928 17817 17817 D SingleTaskActivity: onStart:  Task id 18371
04-18 11:22:34.930 17817 17817 D SingleTaskActivity: onResume:  Task id 18371
04-18 11:22:35.493 17817 17817 D MainActivity: onStop:  Task id 18371

对应的任务栈如下,可以看到,这种方式,并没有创建新的任务栈。不是说好的 系统会创建新任务吗?神奇。。。

Task id #18371....affinity=com.example.launchmodeActivities=[ActivityRecord{e70466d u0 com.example.launchmode/.MainActivity t18368}, ActivityRecord{da03210 u0 com.example.launchmode/.SingleTaskActivity t18368}]

3)只配置taskAffinity

上面两种方式都不能让singleTask这种启动模式创建新的任务栈,那么我们再来看看这种方式,

先配置Activity 的taskAffinity

       <activityandroid:name=".SingleTaskActivity" android:launchMode="singleTop"android:taskAffinity="com.demo.launchmode"/>

对应的生命周期和TaskId如下:

04-18 11:46:13.367 18454 18454 D MainActivity: onCreate:  Task id 18378
04-18 11:46:13.415 18454 18454 D MainActivity: onStart:  Task id 18378
04-18 11:46:13.417 18454 18454 D MainActivity: onResume:  Task id 18378
04-18 11:46:14.973 18454 18454 D MainActivity: ------------- start activity -------------
04-18 11:46:14.989 18454 18454 D MainActivity: onPause:  Task id 18378
04-18 11:46:15.014 18454 18454 D SingleTaskActivity: onCreate:  Task id 18379
04-18 11:46:15.029 18454 18454 D SingleTaskActivity: onStart:  Task id 18379
04-18 11:46:15.032 18454 18454 D SingleTaskActivity: onResume:  Task id 18379
04-18 11:46:15.834 18454 18454 D MainActivity: onStop:  Task id 18378

taskId终于变了。这是为什么呢?

其实,在singleTask模式下,先会检查对应的任务栈是否已经存在,如果不存在就创建对应的任务栈,如果已经存在了就直接复用。由于我们没有指定taskAffinity(默认为包名),所以在1)2)的情况下,就不会创建新的任务栈,直接复用了MainActivity的任务栈。

4)intent设置FLAG_ACTIVITY_NEW_TASK 、配置taskAffinity

对应的周期和Taskid如下:

04-18 11:28:45.628 18164 18164 D MainActivity: onCreate:  Task id 18375
04-18 11:28:45.678 18164 18164 D MainActivity: onStart:  Task id 18375
04-18 11:28:45.681 18164 18164 D MainActivity: onResume:  Task id 18375
04-18 11:28:47.804 18164 18164 D MainActivity: ------------- start activity -------------
04-18 11:28:47.819 18164 18164 D MainActivity: onPause:  Task id 18375
04-18 11:28:47.845 18164 18164 D SingleTaskActivity: onCreate:  Task id 18376
04-18 11:28:47.863 18164 18164 D SingleTaskActivity: onStart:  Task id 18376
04-18 11:28:47.865 18164 18164 D SingleTaskActivity: onResume:  Task id 18376
04-18 11:28:48.666 18164 18164 D MainActivity: onStop:  Task id 18375

这种方式也能创建新的任务栈。

在了解了任务栈的创建时机,我们再来看看实例的复用问题。

5)实例复用(非栈顶)

在对singleTask的作用描述中,有句很重要的话——Activity 一次只能有一个实例存在。怎么理解呢?

我们先启动singleTaskActivity,再启动StandardActivity,最后再启动singleTaskActivity,对应的周期如下:

04-18 12:13:36.873 19652 19652 D SingleTaskActivity: onCreate:  Task id 18391
04-18 12:13:36.903 19652 19652 D SingleTaskActivity: onStart:  Task id 18391
04-18 12:13:36.905 19652 19652 D SingleTaskActivity: onResume:  Task id 18391
04-18 12:13:40.613 19652 19652 D SingleTaskActivity: ------------- start activity -------------
04-18 12:13:40.648 19652 19652 D SingleTaskActivity: onPause:  Task id 18391
04-18 12:13:40.669 19652 19652 D StandardActivity: onCreate:  Task id 18391
04-18 12:13:40.687 19652 19652 D StandardActivity: onStart:  Task id 18391
04-18 12:13:40.689 19652 19652 D StandardActivity: onResume:  Task id 18391
04-18 12:13:41.247 19652 19652 D SingleTaskActivity: onStop:  Task id 18391
04-18 12:13:44.651 19652 19652 D StandardActivity: ------------- start activity -------------
04-18 12:13:44.681 19652 19652 D StandardActivity: onPause:  Task id 18391
04-18 12:13:44.690 19652 19652 D SingleTaskActivity: onNewIntent:  Task id 18391
04-18 12:13:44.691 19652 19652 D SingleTaskActivity: onStart:  Task id 18391
04-18 12:13:44.693 19652 19652 D SingleTaskActivity: onResume:  Task id 18391
04-18 12:13:45.245 19652 19652 D StandardActivity: onStop:  Task id 18391
04-18 12:13:45.248 19652 19652 D StandardActivity: onDestroy:  Task id 18391

通过上面的周期,可以看出最后在启动singleTaskActivity后,StandardActivity走了onDestiny方法,即被退栈。对应的任务栈如下

  Task id #18391....Activities=[ActivityRecord{a81eec8 u0 com.example.launchmode/.SingleTaskActivity t18391}]

整体流程如下

6)实例复用(栈顶)

这种情况与singleTop模式的周期是一致的,如下:

04-18 12:27:44.120 19913 19913 D SingleTaskActivity: onCreate:  Task id 18393
04-18 12:27:44.147 19913 19913 D SingleTaskActivity: onStart:  Task id 18393
04-18 12:27:44.150 19913 19913 D SingleTaskActivity: onResume:  Task id 18393
04-18 12:27:46.043 19913 19913 D SingleTaskActivity: ------------- start activity -------------
04-18 12:27:46.048 19913 19913 D SingleTaskActivity: onPause:  Task id 18393
04-18 12:27:46.049 19913 19913 D SingleTaskActivity: onNewIntent:  Task id 18393
04-18 12:27:46.050 19913 19913 D SingleTaskActivity: onResume:  Task id 18393

五、singleInstance模式

"singleTask" 相似,唯一不同的是系统不会将任何其他 Activity 启动到包含该实例的任务中。该 Activity 始终是其任务唯一的成员;由该 Activity 启动的任何 Activity 都会在其他的任务中打开。

在Android 6.0与Android9.0 上,SingleInstance的实现方式不太一样。

我们先看在Android 6.0上,在A以singleInstance 方式启动B的任务栈,即创建新的Task


Android 9上,在A以singleInstance 方式启动B的任务栈,可见在Android 9 上是直接创建了一个新的Stack和Task。

对应配置文件

        <activityandroid:name=".SingleInstanceActivity"android:launchMode="singleInstance" />

1)intent不设置flag,不配置taskAffinity

直接启动SingleInstanceActivity,对应的周期与TaskId如下:

04-18 12:47:27.360 20390 20390 D MainActivity: onCreate:  Task id 18397
04-18 12:47:27.407 20390 20390 D MainActivity: onStart:  Task id 18397
04-18 12:47:27.410 20390 20390 D MainActivity: onResume:  Task id 18397
04-18 12:47:29.020 20390 20390 D MainActivity: ------------- start activity -------------
04-18 12:47:29.034 20390 20390 D MainActivity: onPause:  Task id 18397
04-18 12:47:29.062 20390 20390 D SingleInstanceActivity: onCreate:  Task id 18398
04-18 12:47:29.076 20390 20390 D SingleInstanceActivity: onStart:  Task id 18398
04-18 12:47:29.079 20390 20390 D SingleInstanceActivity: onResume:  Task id 18398
04-18 12:47:29.882 20390 20390 D MainActivity: onStop:  Task id 18397

神奇,尽管SingleInstanceActivity 没设置taskAffinity,SingleInstanceActivity与MainActivity 的TaskId也会不一样。我们再来看看任务栈情况

这个是SingleInstanceActivity的

  Stack #70: type=standard mode=fullscreenTask id #18398* TaskRecord{4fbb7bd #18398 A=com.example.launchmode U=0 StackId=70 sz=1}affinity=com.example.launchmode....Activities=[ActivityRecord{6a70f2c u0 com.example.launchmode/.SingleInstanceActivity t18398}]

这个是MainActivity的

  Stack #69: type=standard mode=fullscreenTask id #18397* TaskRecord{b3c2503 #18397 A=com.example.launchmode U=0 StackId=69 sz=1}affinity=com.example.launchmode....Activities=[ActivityRecord{83be7c8 u0 com.example.launchmode/.MainActivity t18397}]

我们发现,他们都不在同一个ActivityStack里面了!!!!也就是说,即使没有配置affinity ,singleInstance启动模式也会直接创建新的stack

2)只设置intent为FLAG_ACTIVITY_NEW_TASK

与1)方式一样,这里就不贴出来了

3)只配置taskAffinity

按照如下配置:

   <activityandroid:name=".SingleInstanceActivity"android:launchMode="singleInstance"android:taskAffinity="com.demo.launchdemo"/>

SingleInstanceActivity 的 taskId会变

04-18 14:55:07.879 21977 21977 D MainActivity: onCreate:  Task id 18403
04-18 14:55:07.926 21977 21977 D MainActivity: onStart:  Task id 18403
04-18 14:55:07.929 21977 21977 D MainActivity: onResume:  Task id 18403
04-18 14:55:11.811 21977 21977 D MainActivity: ------------- start activity -------------
04-18 14:55:11.832 21977 21977 D MainActivity: onPause:  Task id 18403
04-18 14:55:11.844 21977 21977 D SingleInstanceActivity: onCreate:  Task id 18404
04-18 14:55:11.863 21977 21977 D SingleInstanceActivity: onStart:  Task id 18404
04-18 14:55:11.866 21977 21977 D SingleInstanceActivity: onResume:  Task id 18404
04-18 14:55:12.700 21977 21977 D MainActivity: onStop:  Task id 18403

我们重点看看SingleInstanceActivity的 ActivityStack

  Stack #76: type=standard mode=fullscreenisSleeping=falsemBounds=Rect(0, 0 - 0, 0)Task id #18404* TaskRecord{4614180 #18404 A=com.demo.launchdemo U=0 StackId=76 sz=1}affinity=com.demo.launchdemo....Activities=[ActivityRecord{801ecc6 u0 com.example.launchmode/.SingleInstanceActivity t18404}]

下面是MainActivity的 ActivityStack

  Stack #75: type=standard mode=fullscreenTask id #18403* TaskRecord{426f3fe #18403 A=com.example.launchmode U=0 StackId=75 sz=1}affinity=com.example.launchmode.....Activities=[ActivityRecord{98c0ac9 u0 com.example.launchmode/.MainActivity t18403}]

可以发现,SingleInstanceActivity也是放在新的ActivityStack中,并且affinity是我们指定的,其他并无差异。

4)intent设置FLAG_ACTIVITY_NEW_TASK 、配置taskAffinity

同样的,这种方式与3)的效果是一致的。

5)成员唯一

我们先启动MainActivity,然后启动SingleInstanceActivity,再启动StandardActivity,来看看任务栈的情况

从下面的TaskId,可以看出,由SingleInstanceActivity启动的StandardActivity 实例与MainActivity是同一个TaskId。

04-18 15:08:16.086 22635 22635 D MainActivity: onCreate:  Task id 18411
04-18 15:08:16.121 22635 22635 D MainActivity: onStart:  Task id 18411
04-18 15:08:16.124 22635 22635 D MainActivity: onResume:  Task id 18411
04-18 15:08:17.948 22635 22635 D MainActivity: ------------- start activity -------------
04-18 15:08:17.963 22635 22635 D MainActivity: onPause:  Task id 18411
04-18 15:08:17.989 22635 22635 D SingleInstanceActivity: onCreate:  Task id 18412
04-18 15:08:18.004 22635 22635 D SingleInstanceActivity: onStart:  Task id 18412
04-18 15:08:18.007 22635 22635 D SingleInstanceActivity: onResume:  Task id 18412
04-18 15:08:18.810 22635 22635 D MainActivity: onStop:  Task id 18411
04-18 15:08:19.502 22635 22635 D SingleInstanceActivity: ------------- start activity -------------
04-18 15:08:19.514 22635 22635 D SingleInstanceActivity: onPause:  Task id 18412
04-18 15:08:19.526 22635 22635 D StandardActivity: onCreate:  Task id 18411
04-18 15:08:19.540 22635 22635 D StandardActivity: onStart:  Task id 18411
04-18 15:08:19.542 22635 22635 D StandardActivity: onResume:  Task id 18411
04-18 15:08:20.398 22635 22635 D SingleInstanceActivity: onStop:  Task id 18412

对应的MainActivity任务栈

  Stack #83: type=standard mode=fullscreenTask id #18411* TaskRecord{8c3ba81 #18411 A=com.example.launchmode U=0 StackId=83 sz=2}affinity=com.example.launchmode.....Activities=[ActivityRecord{52ceb58 u0 com.example.launchmode/.MainActivity t18411}, ActivityRecord{3436465 u0 com.example.launchmode/.StandardActivity t18411}]

SingleInstanceActivity 所处的任务栈如下:

  Stack #84: type=standard mode=fullscreenTask id #18412* TaskRecord{1f72f67 #18412 A=com.example.launchmode U=0 StackId=84 sz=1}affinity=com.example.launchmode.....Activities=[ActivityRecord{7b786ce u0 com.example.launchmode/.SingleInstanceActivity t18412}]

可见,singleInstance所处的任务栈有且仅有一个实例。

6)多SingleInstance情况

这里不禁有一个疑问:ASingleInstanceActivity去启动BSingleInstanceActivity,那BSingleInstanceActivity的实例也是重新创建新的ActivityStack吗?

这里创建一个新的OtherSingleInstanceActivity,启动MainActivity,然后启动SingleInstanceActivity,再启动OtherSingleInstanceActivity

我们再来看看对应的周期和TaskId,可以看出来,三个Activity对应的TaskId都不一样

04-18 15:21:20.029 22908 22908 D MainActivity: onCreate:  Task id 18414
04-18 15:21:20.079 22908 22908 D MainActivity: onStart:  Task id 18414
04-18 15:21:20.082 22908 22908 D MainActivity: onResume:  Task id 18414
04-18 15:21:21.496 22908 22908 D MainActivity: ------------- start activity -------------
04-18 15:21:21.508 22908 22908 D MainActivity: onPause:  Task id 18414
04-18 15:21:21.540 22908 22908 D SingleInstanceActivity: onCreate:  Task id 18415
04-18 15:21:21.563 22908 22908 D SingleInstanceActivity: onStart:  Task id 18415
04-18 15:21:21.565 22908 22908 D SingleInstanceActivity: onResume:  Task id 18415
04-18 15:21:22.350 22908 22908 D MainActivity: onStop:  Task id 18414
04-18 15:21:22.833 22908 22908 D SingleInstanceActivity: ------------- start activity -------------
04-18 15:21:22.845 22908 22908 D SingleInstanceActivity: onPause:  Task id 18415
04-18 15:21:22.875 22908 22908 D OtherSingleInstanceActivity: onCreate:  Task id 18416
04-18 15:21:22.952 22908 22908 D OtherSingleInstanceActivity: onStart:  Task id 18416
04-18 15:21:22.954 22908 22908 D OtherSingleInstanceActivity: onResume:  Task id 18416
04-18 15:21:23.687 22908 22908 D SingleInstanceActivity: onStop:  Task id 18415

我们再来看看OtherSingleInstanceActivity的任务栈

  Stack #88: type=standard mode=fullscreenTask id #18416* TaskRecord{9b24f6 #18416 A=com.example.launchmode U=0 StackId=88 sz=1}affinity=com.example.launchmode....Activities=[ActivityRecord{5037835 u0 com.example.launchmode/.OtherSingleInstanceActivity t18416}]

SingleInstanceActivity的任务栈

 Stack #87: type=standard mode=fullscreenTask id #18415* TaskRecord{7663b64 #18415 A=com.example.launchmode U=0 StackId=87 sz=1}affinity=com.example.launchmode....Activities=[ActivityRecord{384be99 u0 com.example.launchmode/.SingleInstanceActivity t18415}]

MainActivity 的任务栈

 Stack #86: type=standard mode=fullscreenTask id #18414* TaskRecord{ff7f1cd #18414 A=com.example.launchmode U=0 StackId=86 sz=1}affinity=com.example.launchmodeActivities=[ActivityRecord{9dba354 u0 com.example.launchmode/.MainActivity t18414}]

可以看到affinity 一样,但是 ActivityStack id 不一样,说明每一个SingleInstance的Activity独占一个ActivityStack。不像其他三种(Standard,singleTop,singleTask)那样,新建一个TaskRecord。

可见SingleInstance与SingleTask在实现上还是有点的区别的。

六、总结

估计大家都迷糊了,这里做一个总结吧。尤其是singleTask这个模式,与预期的还是有点差别的。

是否创建新的任务 intent不设置flag,不配置taskAffinity 只设置intent为FLAG_ACTIVITY_NEW_TASK 只配置taskAffinity intent设置FLAG_ACTIVITY_NEW_TASK 、配置taskAffinity
Standard模式
SingleTop模式
SingleTask模式
singleInstance模式

回到文章开始提的三个问题,想必大家都知道答案了(不知道的就再看一遍!!!),这里贴一下吧。

  • 问题1:singleTask启动模式,在启动新的Activity的时候,真的会重新创建新的任务栈吗?

    答:不一定,只有指定了taskAffinity ,并且没有对应的任务栈,才会创建对应的任务栈,否则直接复用应用默认的任务栈。

  • 问题2:设置了Intent.FLAG_ACTIVITY_NEW_TASK,每次都会创建新的任务栈吗?

    答:不一定,看Activity所属的任务栈(即Affinity,默认为包名)是否已创建,如果没有就会新建。

  • 问题3:对于singleInstance,显式指定taskAffinity为应用包名,那在启动的时候,还会创建新的任务栈吗?

    答:会的,实际上是新建一个ActivityStack(如果已经存在了,就不创建),里面存放一个对应包的TaskRecord。

画重点

  • onNewIntent 方法中传入的intent是新启动Activity的intent,与getIntent() 方法返回的intent不是同一个intent
  • 在Activity中,可以通过getTaskId()方法获取到TaskId
  • singleTop只进行栈顶的复用,如果不在栈顶,就会创建新的实例
  • SingleInstance在Android 6.0与Android9.0的实现方式已经不一样了,但是概念是一样的,都是创建新的任务栈

文章会比较长,感谢读者能看到最后~
如有不足之处,请多多指教~~

——Weiwq 于 2021.04 广州

Android启动模式详解相关推荐

  1. Android系统(187)---最易懂的Activity启动模式详解

    Android基础:最易懂的Activity启动模式详解 前言 Android基础中,Activity的启动模式非常重要 本文将全面介绍 Activity的启动模式 目录 目录 1. 定义 即Acti ...

  2. Activity的启动模式详解

    Activity的启动模式详解 Activity有四种载入模式:standard(默认), singleTop, singleTask和 singleInstance. (1).standard(默认 ...

  3. Android Activity Launch Mode 启动模式详解

    简介 android:launchMode 属性能设置 Activity 对象启动时是否使用已有的实例还是多次创建,会使得交互设计更加良好,也能避免很多问题. 它是 Androidmanifest.x ...

  4. Android中Activity启动模式详解

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...

  5. Android Activity的4种启动模式详解(示例)

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/5233269.html 先介绍下Android对Activity的管理,Android采用Task来管理多个A ...

  6. Android Activity 启动模式详解

    最近有群里的朋友问我 Activity的四种启动模式分别是什么意思? 当初因为项目比较忙,草草的解释了下, Api文档中说的也只是一般,在这里就小记一下吧,以便有更多的朋友对Activity启动模式了 ...

  7. Activity的5种启动模式详解(新增singleInstancePerTask类型)

    前言 看到这个标题的时候,也许你会认为我写错了,Activity不是只有四种启动模式吗?分别为startard,singleTop,singleTask,singleInstance这四种. 一般来说 ...

  8. Activity生命周期及启动模式详解

    1.Activity生命周期 1.正常情况: (1) onCreate: 表示 Activty 正在被创建,这是 Activity 生命周期的第一个方法,可以做一些初始化的工作,比如:加载布局,绑定控 ...

  9. 活动的四种启动模式详解

    android:launchMode 目录 android:launchMode 前言 概念说明 standard singleTop singleTask singleInstance Codes演 ...

最新文章

  1. 杂项-Log:NLog
  2. map中的erase成员函数用法
  3. Android系统system文件夹结构的说明 \system下的app、lib、bin、etc、fonts、framework
  4. c语言编写贪吃蛇代码无错,刚学C语言,想写一个贪吃蛇的代码
  5. 致家长:疫情期间教育好自己的孩子,就是你最重要的事业!
  6. 矩阵快速幂 POJ 3070 Fibonacci
  7. 阿里云 ssh 登陆请使用(公)ip
  8. 语音识别技术是什么_语音识别技术应用领域介绍
  9. eclipse中文语言包在线安装方法
  10. 科大讯飞语音合成Vue版教程
  11. Linux服务器配置静态IP地址方法
  12. python设置端口控制ie浏览器_[Python爬虫] 之二:Selenium 调用IEDriverServer打开IE浏览器安装配置...
  13. 微信公众号项目(公众号关注登录网站)
  14. linux默认头文件,Linux安装合适内核头文件的方法
  15. 目标跟踪系列三:ECO: Efficient Convolution Operators for Tracking(2016年11月)
  16. 解决pom.xml文件图标变红问题
  17. 网站ftp服务器密码修改,ftp服务器忘记密码修改
  18. 第一周 一起走进摄影世界
  19. 【bzoj4887】[Tjoi2017]可乐 矩阵乘法
  20. 腾讯云IoT边缘服务,助力6000亩沙漠变良田

热门文章

  1. oracle安装清单过不去,oracle 11g(二)安装过程
  2. python 启动django时报错MySQLdb._exceptions.OperationalError: (2059, <NULL>)和django.db.utils.OperationalEr
  3. Abbirb120型工业机器人_工业机器人市场深度调研及投资前景预测报告2020-2024年
  4. linux 内核阻塞,linux内核阻塞IO
  5. 14.QueuedConnection和BlockingQueuedConnection连接方式源码分析
  6. authc过滤器 shiro_Shiro的web下的使用
  7. sinee303a变频器说明书_SINEE--EM303A变频器用户手册.pdf
  8. docker~Dockerfile优化程序的部署
  9. 你还记得当初为什么进入IT行业吗?
  10. 基于matlab的退化图像复原(二)------逆滤波复原