重温 Activity 分析二:启动模式

文章目录
  1. 1. standard 模式
  2. 2. singleTop 模式
  3. 3. singleTask 模式
  4. 4. singleInstance 模式
  5. 5. 参考文献

循序渐进才能赢得时间,细水长流才能直达永恒。

Activity 的生命周期知晓后,其启动模式也是重难点,有时候为了满足项目的特殊需求,必须使用 Activity 的启动模式。本篇继重温 Activity 分析一:生命周期的基础上展开,聊一聊 Activity 中的启动模式。

我们知道,默认情况下,多次启动同一个 Activity 时,系统会创建多个实例,然后把它们一一放入返回栈 (Back Stack) 中。当单击 Back 键时,这些 Activity 会一一回退。返回栈是一种“后进先出”的栈结构,每按下一次 Back 键就会有一个 Activity 出栈,直到栈空为止。当栈中无任何 Activity 的时候,系统便会回收这个返回栈。显然,多次启动同一个 Activity,系统重复创建多个实例,这样不合理。Android 在设计时,提供了 4 种启动模式来丰富系统这一默认行为。

目前这 4 种启动模式分别为:standard、singleTop、singleTask、singleInstance,接下来结合实例一一介绍。代码地址:ActivityLaunchModeDemo & ActivityLaunchModeDemo2

首先定义一个基础 BaseActivity,在其 onCreate() 和 onNewIntent() 方法中打印出当前 Activity 的日志信息,主要包括所在的 task、当前类的 hashCode 和 taskAffinity 的值。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class BaseActivity extends AppCompatActivity {

public static final String TAG = "Iamasoldier6";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Log.d(TAG, "------ onCreate() ------");
Log.d(TAG, "onCreate: " + getClass().getSimpleName() + ", taskId: " + getTaskId() + ", hashCode: "
+ this.hashCode());

dumpTaskAffinity();
}

@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);

Log.d(TAG, "------ onNewIntent() ------");
Log.d(TAG, "onNewIntent: " + getClass().getSimpleName() + ", taskId: " + getTaskId() + ", hashCode: "
+ this.hashCode());

dumpTaskAffinity();
}

protected void dumpTaskAffinity() {
try {
ActivityInfo info = this.getPackageManager().getActivityInfo(getComponentName()
, PackageManager.GET_META_DATA);
Log.d(TAG, "taskAffinity: " + info.taskAffinity);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}

}

standard 模式

标准模式,是默认的启动模式。每次启动一个 Activity 都会创建一个新的实例,无论这个实例是否已经存在。一种典型的多实例实现,一个返回栈中可以有多个实例,每个实例也可以属于不同的返回栈该模式下,谁启动了这个 Activity,这个 Activity 就运行在启动它的那个 Activity 所在的栈中。比如,Activity A 启动了 Activity B (B 是标准模式),则 B 就会进入到 A 所在的栈中。此时,被创建实例的生命周期符合典型情况下 Activity 的生命周期,onCreate()、onStart() 和 onResume() 都会被调用。

配置

1
<activity android:name=".StandardActivity" android:launchMode="standard">

示例

其实,standard 模式中,android:launchMode 可以不用声明,默认就是 standard。

StandardActivity 的代码如下,主界面中点击按钮进入 StandardActivity。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class StandardActivity extends BaseActivity implements View.OnClickListener {

private Button mButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_standard);

mButton = (Button) findViewById(R.id.btn_activity_standard);
mButton.setOnClickListener(this);
}

@Override
public void onClick(View v) {
Intent intent = new Intent(this, StandardActivity.class);
startActivity(intent);
}

}

进入 StandardActivity 后,再点击 2 次其中的按钮。界面如下:

logcat 中的日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
05-29 06:01:36.286 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 06:01:36.287 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: MainActivity, taskId: 664, hashCode: 43714559
05-29 06:01:36.287 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 06:02:11.705 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 06:02:11.706 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: StandardActivity, taskId: 664, hashCode: 166152621
05-29 06:02:11.706 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 06:02:26.611 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 06:02:26.611 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: StandardActivity, taskId: 664, hashCode: 245161438
05-29 06:02:26.612 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 06:02:44.686 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 06:02:44.687 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: StandardActivity, taskId: 664, hashCode: 73602629
05-29 06:02:44.689 27156-27156/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

显然,日志包括 1 次 MainActivity 和 3 次 StandardActivity,从 MainActivity 进入 StandardActivity 时 1 次,后来又点击 2 次其中的按钮,共 3 次 StandardActivity 的日志,其所属返回栈的 taskId 都是 664,即验证了谁启动了这个 Activity,这个 Activity 就运行在启动它的那个 Activity 所在的栈中。启动 StandardActivity 的是 MainActivity,其 taskId 是 664,后续两个 StandardActivity 是被 StandardActivity 启动的,故 taskId 还是 664。此外,每个 Activity 的 hashCode 都是不同的,则它们都是不同的实例,即验证了每次启动一个 Activity 都会创建一个新的实例

singleTop 模式

栈顶复用模式。该模式下,若新的 Activity 已经位于返回栈的栈顶,则此 Activity 不会被重新创建,同时其 onNewIntent() 方法会被回调,通过此方法的参数可以取出当前请求的信息注意,该 Activity 的 onCreate() 和 onStart() 方法不会被系统调用,因为没有发生改变若新 Activity 的实例已经存在但不是位于栈顶,则新 Activity 仍然会重新创建。举例说明,假设目前栈内的情况为 A-B-C-D,其中 A-B-C-D 是 4 个 Activity,A 位于栈底,D 位于栈顶。这时候,要再次启动 D,若 D 具有默认的 ”standard“ 启动模式,则会启动该类的新实例,堆栈会变成 A-B-C-D-D;若 D 的启动模式为 “singleTop”,则 D 的现有实例会通过 onNewIntent() 接收 Intent,因为它位于堆栈的顶部,而堆栈仍然为 A-B-C-D。但是,若收到针对 B 类 Activity 的 Intent,则会向堆栈添加 B 的实例,即便其启动模式为 “singleTop” 也是如此

配置

1
<activity android:name=".SingleTopActivity" android:launchMode="singleTop">

示例

SingleTopActivity 类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class SingleTopActivity extends BaseActivity implements View.OnClickListener {

private Button mButton1, mButton2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singletop);

mButton1 = (Button) findViewById(R.id.btn_activity_singletop);
mButton2 = (Button) findViewById(R.id.btn_activity_other_top);

mButton1.setOnClickListener(this);
mButton2.setOnClickListener(this);
}

@Override
public void onClick(View v) {

switch (v.getId()) {
case R.id.btn_activity_singletop:
Intent intent = new Intent(this, SingleTopActivity.class);
startActivity(intent);
break;

case R.id.btn_activity_other_top:
Intent intent1 = new Intent(this, OtherTopActivity.class);
startActivity(intent1);
break;

default:
break;
}
}

}

OtherTopActivity 类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class OtherTopActivity extends BaseActivity implements View.OnClickListener {

private Button mButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_othertop);

mButton = (Button) findViewById(R.id.btn_activity_other_top);
mButton.setOnClickListener(this);
}

@Override
public void onClick(View v) {
Intent intent = new Intent(this, SingleTopActivity.class);
startActivity(intent);
}

}

界面如下:

logcat 中的日志如下:

1
2
3
4
5
6
7
8
9
10
11
05-29 09:14:25.954 4480-4480/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 09:14:25.956 4480-4480/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: SingleTopActivity, taskId: 672, hashCode: 102765885
05-29 09:14:25.956 4480-4480/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 09:14:32.532 4480-4480/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onNewIntent() ------
05-29 09:14:32.532 4480-4480/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onNewIntent: SingleTopActivity, taskId: 672, hashCode: 102765885
05-29 09:14:32.533 4480-4480/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 09:14:35.018 4480-4480/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onNewIntent() ------
05-29 09:14:35.019 4480-4480/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onNewIntent: SingleTopActivity, taskId: 672, hashCode: 102765885
05-29 09:14:35.019 4480-4480/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

显然,第一次进入 SingleTopActivity 时,输出的是 onCreate() 方法中的日志,后续的都是调用了 onNewIntent() 方法,并未调用 onCreate() 方法,两个日志的 hashCode 相同,则栈中只有一个实例。这是因为第一次进入的时候,栈中没有该实例,创建后,后续两次发现栈顶有该实例,就直接复用,且调用 onNewIntent() 方法。

再看看栈中有实例,但是实例不在栈顶的情况:先从 MainActivity 跳至 SingleTopActivity,然后再跳至 OtherTopActivity,再从 OtherTopActivity 跳回 SingleTopActivity,最后从 SingleTopActivity 跳至本身 SingleTopActivity。

logcat 中的日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
05-29 09:30:53.918 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 09:30:53.918 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: MainActivity, taskId: 678, hashCode: 820289
05-29 09:30:53.918 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 09:32:06.690 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 09:32:06.691 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: SingleTopActivity, taskId: 678, hashCode: 267630492
05-29 09:32:06.691 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 09:32:34.217 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 09:32:34.217 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: OtherTopActivity, taskId: 678, hashCode: 71793903
05-29 09:32:34.218 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 09:33:22.985 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 09:33:22.987 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: SingleTopActivity, taskId: 678, hashCode: 13390986
05-29 09:33:22.987 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 09:33:58.867 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onNewIntent() ------
05-29 09:33:58.869 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onNewIntent: SingleTopActivity, taskId: 678, hashCode: 13390986
05-29 09:33:58.869 23803-23803/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

显然,从 MainActivity 进入到 SingleTopActivity 时,新建了一个 SingleTopActivity 对象,并且 taskId 与 MainActivity 是一致的。接着,从 SingleTopActivity 跳至 OtherTopActivity 时,新建了一个 OtherTopActivity。此时,栈中存在 3 个 Activity,从栈底到栈顶依次为 MainActivity-SingleTopActivity-OtherTopActivity。从上述日志的 hashCode 可以看出,若再跳到 SingleTopActivity,即使栈中已经有 SingleTopActivity 实例,但是仍然会创建一个新的 SingleTopActivity 实例,因为此时 SingleTopActivity 不在栈顶。注意,若再次跳到 SingleTopActivity 时,就会复用栈顶的 SingleTopActivity,即会调用 SingleTopActivity 的 onNewIntent() 方法。

singleTask 模式

栈内复用模式。这是一种单实例模式,在该模式下,只要 Activity 在一个栈中存在,则多次启动此 Activity 都不会重新创建实例,系统会调用现有实例的 onNewIntent() 方法向其传送 Intent。具体一点,当一个具有 singleTask 模式的 Activity A 请求启动后,系统首先会寻找是否存在 A 想要的返回栈。若不存在,就重新创建一个返回栈,然后创建 A 的实例后把 A 放入栈中;若存在 A 所需的返回栈,看 A 是否在栈中有实例存在,如果有实例存在,则系统就会把 A 调到栈顶,并调用它的 onNewIntent() 方法,如果实例不存在,就创建 A 的实例并把 A 压入栈中。此外,这个过程还存在一个返回栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,该任务栈通过 taskAffinity 属性指定。举几个例子:

  • 目前返回栈 S1 中的情况为 A-B-C,这时 Activity D 以 singleTask 模式请求启动,其所需要的返回栈为 S2,因为 S2 和 D 的实例都不存在,故系统会先创建返回栈 S2,再创建 D 的实例,并将其入栈到 S2 中
  • 假设 D 所需要的返回栈为 S1,其他情况如上例,则由于 S1 已存在,故系统会直接创建 D 的实例,并将其入栈到 S1 中
  • 假设 D 所需的返回栈为 S1,并且当前返回栈 S1 的情况为 A-D-B-C,由栈内复用的原则,此时 D 不会重新创建,系统会把 D 切换到栈顶并调用其 onNewIntent() 方法注意,由于 singleTask 默认具有 clearTop 的作用,会导致栈内所有在 D 上面的 Activity 全部出栈,故最终 S1 中的情况为 AD

配置

1
<activity android:name=".SingleTaskActivity" android:launchMode="singleTask">

示例

SingleTaskActivity 类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class SingleTaskActivity extends BaseActivity implements View.OnClickListener {

private Button mButton1, mButton2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singletask);

mButton1 = (Button) findViewById(R.id.btn_activity_singletask);
mButton2 = (Button) findViewById(R.id.btn_activity_other_task);

mButton1.setOnClickListener(this);
mButton2.setOnClickListener(this);
}

@Override
public void onClick(View v) {

switch (v.getId()) {
case R.id.btn_activity_singletask:
Intent intent = new Intent(this, SingleTaskActivity.class);
startActivity(intent);
break;

case R.id.btn_activity_other_task:
Intent intent1 = new Intent(this, OtherTaskActivity.class);
startActivity(intent1);
break;

default:
break;
}
}

}

先不指定任何 taskAffinity 属性,从 MainActivity 进入 SingleTaskActivity,然后跳到 OtherTaskActivity,再跳回到 SingleTaskActivity,界面如下:

logcat 中的日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
05-29 11:14:13.797 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 11:14:13.798 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: MainActivity, taskId: 688, hashCode: 118642126
05-29 11:14:13.798 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 11:10:21.624 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 11:10:21.624 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: SingleTaskActivity, taskId: 688, hashCode: 172100416
05-29 11:10:21.624 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 11:12:10.507 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 11:12:10.508 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: OtherTaskActivity, taskId: 688, hashCode: 237329972
05-29 11:12:10.509 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 11:12:54.284 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onNewIntent() ------
05-29 11:12:54.284 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onNewIntent: SingleTaskActivity, taskId: 688, hashCode: 172100416
05-29 11:12:54.285 16237-16237/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

从 MainActivity 进入到 SingleTaskActivity,再进入 OtherTaskActivity 后,此时栈中有 3 个 Activity 实例,且 SingleTaskActivity 不在栈顶,在 OtherTaskActivity 跳到 SingleTaskActivity 时,并没有创建一个新的 SingleTaskActivity,而是复用了该实例,并回调了 onNewIntent() 方法,原来的 OtherTaskActivity 出栈了。外接模拟器或真机,Terminal 输入adb shell dumpsys activity activities验证如下:

显然,当前栈中只有两个 Activity,原先栈中位于 SingleTaskActivity 之上的 Activity 统统出栈了。

我们观察到,使用 singleTask 模式启动一个 Activity,其还是在原来的 task 中启动。我们并没有指定 taskAffinity 属性,即和默认值包名一样。MainActivity 没有指定 taskAffinity,其启动时创建的 task 名字就是包名。当启动 SingleTaskActivity 时,首先会搜寻其需要的任务栈是否存在,即 taskAffinity 指定的值(这里为包名),存在的话,就不会再创建新的 task,直接使用。此外,该 task 中存在该 Activity 实例时会复用此实例。

换一种情况,若指定 SingleTaskActivity 的 taskAffinity 的值如下:

1
2
<activity android:name=".OtherTaskActivity" android:launchMode="singleTask"
android:taskAffinity="com.iamasoldier6.activitylaunchmodedemo.singletask">

操作如之前,日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
05-29 21:28:46.355 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 21:28:46.356 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: MainActivity, taskId: 695, hashCode: 266150012
05-29 21:28:46.357 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 21:28:56.373 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 21:28:56.373 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: SingleTaskActivity, taskId: 696, hashCode: 247265590
05-29 21:28:56.374 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo.singletask

05-29 21:29:01.723 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onCreate() ------
05-29 21:29:01.723 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onCreate: OtherTaskActivity, taskId: 695, hashCode: 26426314
05-29 21:29:01.725 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo

05-29 21:29:29.994 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: ------ onNewIntent() ------
05-29 21:29:29.994 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: onNewIntent: SingleTaskActivity, taskId: 696, hashCode: 247265590
05-29 21:29:29.995 3700-3700/com.iamasoldier6.activitylaunchmodedemo D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo.singletask

显然,SingleTaskActivity 所属的任务栈的 taskId 发生了变化,即开启了一个新的 task。

此外,可以将两个不同 App 的 Activity 设置为相同的 taskAffinity,虽然在不同的应用中,但是 Activity 会被分配到同一个 task 中。

singleInstance 模式

单实例模式, 一种加强的 singleTask 模式。除了具有 singleTask 模式的所有特性,还新增一点,具有该模式的 Activity 只能单独地位于一个返回栈中,具有全局唯一性,即整个系统中就这么一个实例。比如,Activity A 是 singleInstance 模式,当 A 启动后,系统会为它创建一个新的返回栈,然后 A 独自在这个新的返回栈中。若再启动这样的 Activity 时,已经存在一个实例,则会把它所在的任务调度到前台,重用这个实例。因为具有栈内复用的特点,接下来的请求都不会创建新的 Activity,除非该独特的任务栈被系统销毁掉。

新建一个 App 名为 ActivityLaunchModeDemo2,MainActivity 仍然继承自 BaseActivity,不在话下。看看 SingleInstanceActivity:

配置

1
2
3
4
5
6
7
8
<activity android:name=".SingleInstanceActivity"
android:launchMode="singleInstance">


<intent-filter>
<action android:name="com.iamasoldier6.activitylaunchmodedemo2.singleinstance"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>

示例

1
2
3
4
5
6
7
8
9
public class SingleInstanceActivity extends BaseActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singleinstance);
}

}

在两个 App 中设置启动的代码为:

1
2
3
Intent intent = new Intent();
intent.setAction("com.iamasoldier6.activitylaunchmodedemo2.singleinstance");
startActivity(intent);

界面如下:

logcat 中的日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// ActivityLaunchModeDemo2 中打开
05-29 23:28:01.519 31192-31192/com.iamasoldier6.activitylaunchmodedemo2 D/Iamasoldier6: ------ onCreate() ------
05-29 23:28:01.521 31192-31192/com.iamasoldier6.activitylaunchmodedemo2 D/Iamasoldier6: onCreate: MainActivity, taskId: 713, hashCode: 266150012
05-29 23:28:01.523 31192-31192/com.iamasoldier6.activitylaunchmodedemo2 D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo2

05-29 23:28:08.702 31192-31192/com.iamasoldier6.activitylaunchmodedemo2 D/Iamasoldier6: ------ onCreate() ------
05-29 23:28:08.702 31192-31192/com.iamasoldier6.activitylaunchmodedemo2 D/Iamasoldier6: onCreate: SingleInstanceActivity, taskId: 714, hashCode: 11051282
05-29 23:28:08.703 31192-31192/com.iamasoldier6.activitylaunchmodedemo2 D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo2

// ActivityLaunchModeDemo 中打开
05-29 23:28:27.744 31192-31192/com.iamasoldier6.activitylaunchmodedemo2 D/Iamasoldier6: ------ onNewIntent() ------
05-29 23:28:27.745 31192-31192/com.iamasoldier6.activitylaunchmodedemo2 D/Iamasoldier6: onNewIntent: SingleInstanceActivity, taskId: 714, hashCode: 11051282
05-29 23:28:27.745 31192-31192/com.iamasoldier6.activitylaunchmodedemo2 D/Iamasoldier6: taskAffinity: com.iamasoldier6.activitylaunchmodedemo2

显然,ActivityLaunchModeDemo2 启动 SingleInstanceActivity 时,因为系统中不存在这个实例,故新建了一个 task。按 Home 键,使用 ActivityLaunchModeDemo,点击按钮进入 SingleInstanceActivity,因为系统中已经存在了一个实例,则不会再创建新的 task,直接复用该实例,并且回调 onNewIntent() 方法,从其 hashCode 中可以看出是同一个实例,即 SingleInstance 模式启动的 Activity 在系统中具有全局唯一性

至此,关于 Activity 的启动模式归纳分析完毕,重温 Activity 系列两篇文章到此结束。

本人才疏学浅,如有疏漏错误之处,望读者中有识之士不吝赐教,谢谢。

1
Email: [email protected] / WeChat: Wolverine623

您也可以关注我个人的微信公众号码农六哥第一时间获得博客的更新通知,或后台留言与我交流

参考文献

1.https://developer.android.google.cn/guide/components/tasks-and-back-stack.html?hl=zh-cn

2.Android 开发艺术探索

3.http://blog.csdn.net/mynameishuangshuai/article/details/51491074