学习安卓,首先就要接触和学习Activity,想必大家在学习activity的过程中一定对activity的launchmode有过困惑。好在网络上关于activity launchmode的博客、解释一大堆,可以方便我们去理解和使用activity launchmode模式,但我强烈建议大家还是要自己动手实践做一些例子,这样才能真正理解activity的launchmode,而且经过实践后你会发现,原来网上的那些解释有很多都是错的、过时的,通过亲自实践得出的结论才是真正被理解的,正确的。
闲话少说,我们来看下官方文档列出的四种Activity的启动模式:
系统在启动 Activity 的任务中创建 Activity 的新实例并向其传送 Intent。Activity 可以多次实例化,而每个实例均可属于不同的task,并且一个task可以拥有多个实例。
如果当前task的顶部已存在 Activity 的一个实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建 Activity 的新实例。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例(但前提是位于返回栈顶部的 Activity 并不是 Activity 的现有实例)。
系统创建新task并实例化将其置于新task底部。但是,如果该 Activity 的一个实例已存在于任何一个单独的任务中,则系统会通过调用现有实例的 onNewIntent() 方法向其传送 Intent,而不是创建新实例。一次只能存在 Activity 的一个实例。
与 “singleTask” 相同,只是系统不会将任何其他 Activity 启动到包含singleInstance实例的task中。该 Activity 始终是其task唯一仅有的成员。
好了,这些是官方文档的说明,这里我要强调两个概念:任务 task 和 返回栈(任务栈)back stack 。任务是指与用户交互的一系列 Activity的集合, 这些Activity 按照各自的打开顺序排列在一个返回栈中,而返回栈(任务栈)则是一个栈,存储一个或多个任务。这里千万不要搞混,有些朋友认为二者是一个东西,就是一个“任务栈”,例如,在singleTask模式中,会新建一个“任务”,而不是新建一个“任务栈”,这个任务在不在一个新的任务栈里还有待商榷,下文会详细介绍。
standard模式自然不用多说,也没什么争议,只要通过intent进行启动,就会创建一个新Activity实例,即使是在intent Flag设定为FLAG_ACTIVITY_CLEAR_TOP时,也会将原有的activity实例干掉,重新创建一个activity。singleTop也好理解,如果当前task的顶部存在该activity的实例就不会创建新的实例,而是调用activity的onNewIntent方法。
关于singleTask,网上的说法则有很多种,很多说法是当activity不存在任何一个任务中时,采用singleTask方法启动会单独建立一个任务栈,activity放在新任务栈的栈底。这种说法不对的,首先,在activity中有个taskAffinity(任务亲和力)的概念,官方文档对taskAffinity的说法是:除有特殊情况(conceptually 概念上,我不知道咋翻译)有相同taskAffinity的activity属于同一个任务(Activities with the same affinity conceptually belong to the same task)。而taskAffinity的优先级要比singleTask、Intent.FLAG_ACTIVITY_NEW_TASK要高,当两个activity有相同的taskAffinity时,singleTask并不会新建一个任务栈,它只是当该activity不存在于任何一个任务中时,在当前任务栈中新建一个activity,新建立的activity和当前app原有的任务在一个相同的任务栈中,当新建的activity的taskaffinity与之前不同时,才会新建返回栈并将activity放置其中。如果该Activity的一个实例已存在于一个单独的任务中,则系统会通过调用现有实例的 onNewIntent() 方法向其传送 Intent。而singleInstance不同,它“无视”taskAffinity,将activity启动到新的任务栈中,并且该 Activity 始终是其任务唯一仅有的成员。这里我们可以做个小例子验证一下:
这里我创建了三个Activity:Activity1 、Activity2、Activity3。Activity1中包含了一个button,会跳转到Activity2,Activity2有两个button,一个跳到Activity1,一个会跳到Activity3。Activity3和Activiyt1一样,只包含了一个button,可以跳转到Activity2。代码和布局如下:
Activity1:
public class Activity1 extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_task1);
Log.v("lzq", "activity 1 onCreate");
Log.v("lzq", "activity 1 is task root? " + isTaskRoot());
}
public void toActivity2(View view) {
startActivity(new Intent(this, Activity2.class));
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.v("lzq", "activity 1 onNewIntent");
}
@Override
protected void onResume() {
super.onResume();
Log.v("lzq", "activity 1 onResume");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.v("lzq", "activity 1 onDestroy");
}
}
Activity2:
public class Activity2 extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_task2);
Log.v("lzq", "activity 2 onCreate");
Log.v("lzq", "activity 2 is task root? " + isTaskRoot());
}
public void toActivity3(View view) {
startActivity(new Intent(this, Activity3.class));
}
public void toActivity1(View view) {
startActivity(new Intent(this, Activity1.class));
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.v("lzq", "activity 2 onNewIntent");
}
@Override
protected void onResume() {
super.onResume();
Log.v("lzq", "activity 2 onResume");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.v("lzq", "activity 2 onDestroy");
}
}
public class Activity3 extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_task3);
Log.v("lzq", "activity 3 onCreate");
Log.v("lzq", "activity 3 is task root? " + isTaskRoot());
}
public void toActivity2(View view) {
startActivity(new Intent(this, Activity2.class));
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.v("lzq", "activity 3 onNewIntent");
}
@Override
protected void onResume() {
super.onResume();
Log.v("lzq", "activity 3 onResume");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.v("lzq", "activity 3 onDestroy");
}
}
Activity3布局:
mainfests.xml配置如下:
<activity
android:name=".singletask.Activity1"
android:launchMode="standard"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
<activity android:name=".singletask.Activity2"
android:launchMode="singleTask"
>
activity>
<activity android:name=".singletask.Activity3"
android:launchMode="singleInstance"
>
activity>
注意,Activity1 launchmode为standard,Activity2为singleTask,Activity3的launchmode为singleInstance。进入app会打印以下log:
12-03 14:59:06.097 12823-12823/lizongquan.test.com.test4 V/lzq: activity 1 onCreate
12-03 14:59:06.117 12823-12823/lizongquan.test.com.test4 V/lzq: activity 1 is task root? true
12-03 14:59:06.117 12823-12823/lizongquan.test.com.test4 V/lzq: activity 1 onResume
Activity中的isTaskRoot()方法用于判断是否当前的activity在任务栈的栈底,可见,Activity1位于任务栈的栈底,点击返回键时,该app会退出。当点击toActivity2 button时:
12-03 15:06:46.587 12823-12823/lizongquan.test.com.test4 V/lzq: activity 2 onCreate
12-03 15:06:46.587 12823-12823/lizongquan.test.com.test4 V/lzq: activity 2 is task root? false
12-03 15:06:46.587 12823-12823/lizongquan.test.com.test4 V/lzq: activity 2 onResume
可见,Activity2不位于任务栈的栈底,它与Activity1位于同一个任务栈中。也就是说,在taskAffinity相同的情况下,当activity不在任何一个task中存在时,singleTask启动模式并不会为其新建立一个任务栈,只会在当前的任务栈中新建这个activity。再次点击toActivity1按钮可以发现:
12-03 15:12:46.157 12823-12823/lizongquan.test.com.test4 V/lzq: activity 1 onCreate
12-03 15:12:46.157 12823-12823/lizongquan.test.com.test4 V/lzq: activity 1 is task root? false
12-03 15:12:46.157 12823-12823/lizongquan.test.com.test4 V/lzq: activity 1 onResume
Log显示系统又新建立了个Activity1实例,它在任务栈的位置位于activity2之上。这也验证了以standard模式启动,不论该activity实例是否存在,系统都会创建新实例。这时页面已经在Activity1中,我们再次点击toActivity2:
12-03 15:18:32.967 12823-12823/lizongquan.test.com.test4 V/lzq: activity 2 onNewIntent
12-03 15:18:32.967 12823-12823/lizongquan.test.com.test4 V/lzq: activity 2 onResume
12-03 15:18:33.347 12823-12823/lizongquan.test.com.test4 V/lzq: activity 1 onDestroy
可以看到,activity2执行了它的onNewIntent方法,没有创建新的实例,并将activity2任务栈上面的activity1干掉。然后点击Activity3:
12-03 15:24:53.707 6339-6339/lizongquan.test.com.test4 V/lzq: activity 3 onCreate
12-03 15:24:53.707 6339-6339/lizongquan.test.com.test4 V/lzq: activity 3 is task root? true
12-03 15:24:53.707 6339-6339/lizongquan.test.com.test4 V/lzq: activity 3 onResume
可以看到,activity3的isTaskRoot = true,证明singleInstance会将activity放入新的任务栈中,并不会与之前的task放在一个任务栈中,而当我们点击toActivity2的时候:
12-03 15:27:22.067 6339-6339/lizongquan.test.com.test4 V/lzq: activity 2 onNewIntent
12-03 15:27:22.067 6339-6339/lizongquan.test.com.test4 V/lzq: activity 2 onResume
可见,Activity3并没有销毁,这也再次说明了Activity3并没有和Activity2位于一个任务栈中。
上面描述的都是activity具有相同taskAffinity情况。在不同的taskAffinity情况下singleTask表现会截然不同,在这里我将Activity2和Activity3都设置为不同的taskAffinity,并且Activity2为singleTask的,Activity3换成了standard,其它代码不变,mainifests.xml配置如下:
<activity
android:name=".singletask.Activity1"
android:launchMode="standard"
android:label="@string/app_name"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
<activity android:name=".singletask.Activity2"
android:launchMode="singleTask"
android:taskAffinity="lizongquan.test.com.test5"
>
activity>
<activity android:name=".singletask.Activity3"
android:launchMode="standard"
android:taskAffinity="lizongquan.test.com.test6"
>
activity>
从Activity1点击toActivity2,log显示:
12-08 14:49:25.286 12010-12010/lizongquan.test.com.test4 V/lzq: activity 1 onCreate
12-08 14:49:25.286 12010-12010/lizongquan.test.com.test4 V/lzq: activity 1 is task root? true
12-08 14:49:25.286 12010-12010/lizongquan.test.com.test4 V/lzq: activity 1 onResume
12-08 14:49:37.376 12010-12010/lizongquan.test.com.test4 V/lzq: activity 2 onCreate
12-08 14:49:37.376 12010-12010/lizongquan.test.com.test4 V/lzq: activity 2 is task root? true
12-08 14:49:37.376 12010-12010/lizongquan.test.com.test4 V/lzq: activity 2 onResume
可以看到,activity 2 的isTaskRoot为true,说明Activity2被放在单独的一个任务栈里了。再点击toActivity3:
12-08 14:51:35.596 12010-12010/lizongquan.test.com.test4 V/lzq: activity 3 onCreate
12-08 14:51:35.596 12010-12010/lizongquan.test.com.test4 V/lzq: activity 3 is task root? false
12-08 14:51:35.596 12010-12010/lizongquan.test.com.test4 V/lzq: activity 3 onResume
可见,虽然Activity3属于不同的taskAffinity,Activity3并没有开辟一个新的任务栈,事实上,因为我们是通过Activity2调用的Activity3,所以Activity3与Activity2会在一个任务栈中,点击toActivity2进行验证:
12-08 14:55:45.516 12010-12010/lizongquan.test.com.test4 V/lzq: activity 2 onNewIntent
12-08 14:55:45.516 12010-12010/lizongquan.test.com.test4 V/lzq: activity 2 is task root? true
12-08 14:55:45.516 12010-12010/lizongquan.test.com.test4 V/lzq: activity 2 onResume
12-08 14:55:45.886 12010-12010/lizongquan.test.com.test4 V/lzq: activity 3 onDestroy
可以看到,Activity3被干掉了(被clear top)。证明Activity3与Activity2在一个任务栈中。
总结:创建新Activity时是否创建新的任务栈由activity的launchmode(或调用activity的Intent Flag)决定,如果launchmode为singleTask模式或singleInstance,则会“倾向”于建立一个新的任务栈。当launchmode为singleTask模式时,taskAffinity优先级会更高,它来决定是否会建立一个新的任务栈,如新的activity taskAffinity与之前的相同,则不会建立新任务栈,taskAffinity与之前不同则建立新的任务栈。而singleInstance模式无视taskAffinity,建立新的任务栈,不管taskAffinity如何,standard模式和singleTop模式都不会建立新的任务栈。
Intent Flag分析
在分析Intent Flag之前,不得不提的是,android关于activity和task还存在许多bug(Android has TONS of bugs related to activities and tasks.)建议大家在遇到关于task问题的时候,优先去stackOverFlow查看前人的教训以免入坑。
与activity launchmode有关的Intent Flag有如下几种:
FLAG_ACTIVITY_CLEAR_TOP
如果设置了FLAG_ACTIVITY_CLEAR_TOP,并且这个Activity已经在当前的Task中运行,这个activity不会被重新加载,而是关闭该Activity上方的所有Activity,然后将这个Intent发送给这个activity。
FLAG_ACTIVITY_SINGLE_TOP
/**
* If set, the activity will not be launched if it is already running
* at the top of the history stack.
*/
可见,FLAG_ACTIVITY_SINGLE_TOP与launchmode的singleTop功能相同。
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_NEW_TASK 的作用效果与singleTask类似,都会判断调用Activity的实例是否存在,如存在则不会新建立实例,而是发送intent给该activity,不存在则会建立一个新的实例。但不同的是,FLAG_ACTIVITY_NEW_TASK不会将被调用activity上层的activity干掉,它与FLAG_ACTIVITY_CLEAR_TOP 联用就可以达到类似singleTask的效果。
值得注意的是,与singleTask一样,FLAG_ACTIVITY_NEW_TASK的优先级要比taskAffinity低,相同的taskAffinity采用FLAG_ACTIVITY_NEW_TASK调用并不会新建立任务栈。此外,采用FLAG_ACTIVITY_NEW_TASK时,为了不创建新的实例而是调用onNewIntent()方法,还需加上FLAG_ACTIVITY_SINGLE_TOP标记(见我的博客——Activity onNewIntent方法浅析)。所以,要达到singleTask的效果我们需要这么做:
Intent intent = new Intent(this,OtherActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);