四种启动模式
Activity会保存在Activity栈中,按back键,Activity会按照“后进先出”的顺序退栈,当栈中没有Activity时,系统会回收这个任务栈。
默认情况下,Activity使用默认的启动模式,多次启动同一个Activity,系统会重复创建多个实例。Android提供了4种启动模式来修改系统的默认行为。
standard、singleTop、singleTask和singleInstance
standard:标准模式,系统的默认模式。每次启动 Activity 都创建一个新的实例,不管这个实例是否已经存在。创建实例,会走 Activity 的onCreate、onStart、onResume方法
这是一个多实例实现,一个 Activity 栈中可以有多个实例,每个实例也可以属于不同的 Activity 栈singleTop:栈顶复用模式。如果新的 Activity 已经位于栈顶,Activity 不会被重建,会回调它的 onNewIntent 方法,不会回调 onCreate 和 onStart 方法。如果新的 Activity 实例存在于 Activity 栈中,但是不在栈顶,还是会创建新的实例。
singleTask:栈内复用,是一种单例模式。这种模式下,只要 Activity 在一个栈中存在,那么多次启动该 Activity 都不会重新创建实例,会回调 onNewIntent 方法。
具有 singleTask 模式的 Activity A 请求启动,系统先寻找是否存在 A 想要的任务栈:
如果不存在,创建一个任务栈,创建 A 的实例,把 A 放栈中;
如果存在,要看该栈中是否有 A 的实例:
如果存在 A 的实例,把A调到栈顶,并调用它的 onNewIntent 方法;
如果不存在 A 的实例,创建 A 的实例,把 A 压入栈中
如果 Activity D 以 singleTask 模式启动,需要的任务栈是 S1,并且当前 Activity 栈 S1 的情况是 ADBC,C 在栈顶,系统会把 D 切换到栈顶并调用 onNewIntent 方法,singleTask 有 clearTop 的效果,将 D 切换到栈顶,所有在 D 上边的 Activity B C 全部出栈,S1 的栈中为 ADsingleInstance 单例模式。除了具有 singleTask 的特性外,具有 singleInstance 的 Activity 单独位于一个 Activity 栈中。
Activity A是 singleInstance 模式,当 A 启动后,系统会为它创建一个新的任务栈,然后 A 独自在这个任务栈中,后续请求 Activity A,都不会新建 Activity A,除非这个任务栈被系统销毁。
有2个栈,前台Activity栈存的是 AB,后台Activity栈存的是 CD,启动模式是 singleTask。
请求启动D,后台任务栈被切换到前台,顺序变成了 ABCD,按 back,会一一出栈。
如果请求启动的 不是 D,而是 C,如下图
什么是Activity的任务栈?
从一个参数说起:TaskAffinity,标识了一个Activity所需要的任务栈的名字。默认情况下,Activity 的任务栈名字是应用包名,可以为每个 Activity 指定 TaskAffinity。
TaskAffinity 属性和 singleTask 模式或者 allowTaskReparenting 属性配对使用。
任务栈分为 前台任务栈和后台任务栈,后台任务栈中的 Activity 处于暂停状态,用户可以通过切换 将后台任务栈再次调用到前台。TaskAffinity 和 singleTask 配对使用时
当 Activity 以 singleTask 模式启动时,TaskAffinity 保存的该 Activity 当前任务栈的名字,待启动的Activity会运行在 名字和 TaskAffinity 相同的任务栈中。TaskAffinity 和 allowTaskReparenting 结合使用
应用 A 启动应用 B 的Activity b,如果 Activity b 的 allowTaskReparenting 属性为 true,那么 当应用 B 被启动后,Activity b 会从 应用 A 的任务栈 转移到 应用 B 的任务栈。
也就是说 应用 A 启动了 应用 B 的Activity b, 此时 b 在 A 的任务栈中,按 Home 键回到桌面,再单击B的图标,这个时候打开的Activity 不是B 的 MainActivity,显示的是 被 A 启动的 Activity b。Activity b 从 A 的任务栈 转到了 B 的任务栈。
设置Activity的启动模式
- manifest中设置
<activity android:name=".BActivity"
android:launchMode="singleTask">
- 代码中Intent设置flag
Intent intent = new Intent();
intent.setClass(MainActivity.this, BActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Manifest 和 代码 同时 设置启动模式时,以代码为准。
Manifest中 没有 Intent.FLAG_ACTIVITY_CLEAR_TOP 标识;代码里没有 singleInstance
对比singleTask与默认模式
1。singleTask
Mainifest
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
MainActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, MainActivity.class);
intent.putExtra("time", System.currentTimeMillis());
startActivity(intent);
}
});
Log.d(TAG, "onCreate::");
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG, "onNewIntent::, time = " + intent.getLongExtra("time", 0));
}
log
//第一次运行
01-09 11:43:57.279 22595-22595/? D/MainActivity_A: onCreate::
01-09 11:43:57.284 22595-22595/? D/MainActivity_A: onStart::
01-09 11:43:57.287 22595-22595/? D/MainActivity_A: onResume::
//点击button
01-09 11:44:04.184 22595-22595/? D/MainActivity_A: onPause::
01-09 11:44:04.184 22595-22595/? D/MainActivity_A: onNewIntent::, time = 1483933444173
01-09 11:44:04.186 22595-22595/? D/MainActivity_A: onResume::
//点击button
01-09 11:44:05.602 22595-22595/? D/MainActivity_A: onPause::
01-09 11:44:05.602 22595-22595/? D/MainActivity_A: onNewIntent::, time = 1483933445593
01-09 11:44:05.606 22595-22595/? D/MainActivity_A: onResume::
//点击button
01-09 11:44:07.035 22595-22595/? D/MainActivity_A: onPause::
01-09 11:44:07.035 22595-22595/? D/MainActivity_A: onNewIntent::, time = 1483933447025
01-09 11:44:07.037 22595-22595/? D/MainActivity_A: onResume::
adb shell dumpsys activity activities > d:\temp\activity_single.txt
Running activities (most recent first):
TaskRecord{44d3a3a #828 A=activity.jieqiong.com.activity U=0 StackId=1 sz=1}
Run #0: ActivityRecord{a01bb0d u0 activity.jieqiong.com.activity/.MainActivity t828}
2。默认模式,去掉Manifest中的android:launchMode=”singleTask”
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
log
//第一次运行
01-09 11:49:45.197 27333-27333/? D/MainActivity_A: onCreate:: test =
01-09 11:49:45.203 27333-27333/? D/MainActivity_A: onStart::
01-09 11:49:45.214 27333-27333/? D/MainActivity_A: onResume::
//点击button
01-09 11:49:47.691 27333-27333/? D/MainActivity_A: onPause::
01-09 11:49:47.718 27333-27333/? D/MainActivity_A: onCreate:: test =
01-09 11:49:47.719 27333-27333/? D/MainActivity_A: onStart::
01-09 11:49:47.720 27333-27333/? D/MainActivity_A: onResume::
01-09 11:49:48.115 27333-27333/? D/MainActivity_A: onSaveInstanceState:: save test = testString
01-09 11:49:48.118 27333-27333/? D/MainActivity_A: onStop::
//点击button
01-09 11:49:49.007 27333-27333/? D/MainActivity_A: onPause::
01-09 11:49:49.051 27333-27333/? D/MainActivity_A: onCreate:: test =
01-09 11:49:49.052 27333-27333/? D/MainActivity_A: onStart::
01-09 11:49:49.054 27333-27333/? D/MainActivity_A: onResume::
01-09 11:49:49.470 27333-27333/? D/MainActivity_A: onSaveInstanceState:: save test = testString
01-09 11:49:49.471 27333-27333/? D/MainActivity_A: onStop::
//点击button
01-09 11:49:50.365 27333-27333/? D/MainActivity_A: onPause::
01-09 11:49:50.401 27333-27333/? D/MainActivity_A: onCreate:: test =
01-09 11:49:50.403 27333-27333/? D/MainActivity_A: onStart::
01-09 11:49:50.407 27333-27333/? D/MainActivity_A: onResume::
01-09 11:49:50.844 27333-27333/? D/MainActivity_A: onSaveInstanceState:: save test = testString
01-09 11:49:50.845 27333-27333/? D/MainActivity_A: onStop::
adb shell dumpsys activity activities > d:\temp\activity.txt
Running activities (most recent first):
TaskRecord{21795c4 #829 A=activity.jieqiong.com.activity U=0 StackId=1 sz=4}
Run #3: ActivityRecord{b7d9564 u0 activity.jieqiong.com.activity/.MainActivity t829}
Run #2: ActivityRecord{77d3a1e u0 activity.jieqiong.com.activity/.MainActivity t829}
Run #1: ActivityRecord{5c6e3b3 u0 activity.jieqiong.com.activity/.MainActivity t829}
Run #0: ActivityRecord{830e7ba u0 activity.jieqiong.com.activity/.MainActivity t829}
singleTask举例
- 默认模式的 MainActivity 按钮 BtnA,点击 打开 BActivity(singleTop,指定栈为com.jieqiong.task1)
- 点击BActivity的按钮BtnB,打开 CActivity(singleTop,指定栈为com.jieqiong.task1)
- 点击CActivity的按钮BtnC,打开MainActivity
- 点击MainActivity的按钮BtnA,打开BActivity
- 按back,直至回到桌面
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".BActivity"
android:launchMode="singleTask"
android:taskAffinity="com.jieqiong.task1">
</activity>
<activity android:name=".CActivity"
android:launchMode="singleTask"
android:taskAffinity="com.jieqiong.task1">
</activity>
MainActivity——BActivity——CActivity——MainActivity对应的Activity栈如下:
此时是2个栈,com.jieqiong.task1中存从是BCMain,B在栈底及 包名栈中存的是Main。
adb shell dumpsys activity activities
//BActivity,CActivity是singleTask的,栈名是com.jieqiong.task1,通过CActivity启动MainActivity,此时的MainActivity在栈task1中,不是包名的栈
Running activities (most recent first):
TaskRecord{9598382 #894 A=com.jieqiong.task1 U=0 StackId=1 sz=3}
Run #6: ActivityRecord{2fa1a85 u0 activity.jieqiong.com.activity/.MainActivity t894}
Run #5: ActivityRecord{f23f6b3 u0 activity.jieqiong.com.activity/.CActivity t894}
Run #4: ActivityRecord{8ff2e5e u0 activity.jieqiong.com.activity/.BActivity t894}
//第一次启动的MainActivity,模式是standard,栈是包名
TaskRecord{ef33ad0 #893 A=activity.jieqiong.com.activity U=0 StackId=1 sz=1}
Run #3: ActivityRecord{b53b587 u0 activity.jieqiong.com.activity/.MainActivity t893}
TaskRecord{ea055ce #834 A=com.android.settings U=0 StackId=1 sz=3}
Run #2: ActivityRecord{67aa406 u0 com.android.settings/.SubSettings t834}
Run #1: ActivityRecord{cb7a39 u0 com.android.settings/.SubSettings t834}
Run #0: ActivityRecord{aaa95a9 u0 com.android.settings/.Settings t834}
此时点击MainActivity的BtnB,要启动B,会将com.jieqiong.task1栈中的B移到顶部,上边的C和Main先出栈,该栈中只剩下B,按Back,B出栈,task1栈清空;退到 包名的栈,显示MainActivity,按Back,MainActivity从包名栈中出栈,回到桌面。
启动Activity的flag
//同一个任务栈中位于该Activity上边的,都要出栈,一般和FLAG_ACTIVITY_NEW_TASK配合使用,如果该Activity实例已存在,会调用onNewIntent方法。
//如果该Activity使用的是standard模式,它和它上边的Activity都出栈,系统创建新的实例,放入栈顶
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//该Activity不会出现在历史Activity列表中,等同于在Manifest中为该Activity加 android:excludeFromRecents="true"
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);