Activity: launchMode 和 Intent.FLAG_ACTIVITY_CLEAR_TOP

Activity 的 launchMode:

1. standard: 标准模式

这种启动模式为标准模式,也是默认模式。每当我们启动一个Activity,系统就会相应的创建一个实例,不管这个实例是否已经存在。这种模式,一个栈中可以有多个实例,每个实例也都有自己的任务栈。而且是谁启动了此Activity,那么这个Activity就运行在启动它的Activity所在的栈中。

例:有Activity 1 --> Activity 1 -> Activity 1-> Activity 1 -> Activity 1 

Logcat:

2019-09-19 09:24:28.937 12796-12796/? I/xp.chen: onCreate: 1 Activity
2019-09-19 09:24:29.944 12796-12796/? I/xp.chen: onCreate: 1 Activity
2019-09-19 09:24:30.527 12796-12796/? I/xp.chen: onCreate: 1 Activity
2019-09-19 09:24:30.978 12796-12796/? I/xp.chen: onCreate: 1 Activity
2019-09-19 09:24:31.332 12796-12796/? I/xp.chen: onCreate: 1 Activity

2. singleTop:栈顶复用模式

这种启动模式下,如果要启动的Activity已经处于栈的顶部,那么此时系统不会创建新的实例,而是直接打开此页面,同时它的onNewIntent()方法会被执行,我们可以通过Intent进行传值,而且它的onCreate(),onStart()方法不会被调用,因为它并没有发生任何变化。

3. singleTask:栈内复用模式

在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,因为singleTask本身自带clearTop这种功能。并且会回调该实例的onNewIntent()方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。不设置taskAffinity属性的话,默认为应用的包名。

例:有Activity 1 --> Activity 2 -> Activity 3-> Activity 4 -> Activity 5 -> Activity 1, 其中Activity 1 设置成singleTask属性,在Activity5中设置跳转到Activity 1.

Logcat:

2019-09-19 10:46:07.415 16400-16400/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 1 Activity
2019-09-19 10:46:10.476 16400-16400/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 2 Activity
2019-09-19 10:46:11.105 16400-16400/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 3 Activity
2019-09-19 10:46:11.714 16400-16400/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 4 Activity
2019-09-19 10:46:12.653 16400-16400/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 5 Activity
2019-09-19 10:46:15.388 16400-16400/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 2 Activity
2019-09-19 10:46:15.395 16400-16400/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 3 Activity
2019-09-19 10:46:15.403 16400-16400/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 4 Activity
2019-09-19 10:46:15.424 16400-16400/com.yongdaimi.android.androidapitest I/xp.chen: onNewIntent: 1 Activity
2019-09-19 10:46:15.698 16400-16400/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 5 Activity

可以看到在这种模式下,Activity 2 ~ Activity 5都会被销毁(包括Activity 5), 然后会调用Activity 1的onNewIntent()方法。利用这种模式的特性,可以实现退出应用,比如在Activity 5启动Activity时,传入一个参数:

public void next(View view) {
        Intent intent = new Intent(getApplicationContext(), LaunchMode1Activity.class);
        intent.putExtra(LaunchMode1Activity.EXTRA_FINISH, 0);
        startActivity(intent);
}

然后在Activity 1的onNewIntent()方法中做处理:

@Override
    protected void onNewIntent(Intent intent)
    {
        super.onNewIntent(intent);
        Log.i("xp.chen", "onNewIntent: 1 Activity");
        int intExtra = intent.getIntExtra(EXTRA_FINISH, -1);
        if (intExtra == 0) finish();
    }

运行Logcat如下:

2019-09-19 10:51:11.911 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 1 Activity
2019-09-19 10:51:13.419 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 2 Activity
2019-09-19 10:51:13.848 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 3 Activity
2019-09-19 10:51:14.385 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 4 Activity
2019-09-19 10:51:16.874 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 5 Activity
2019-09-19 10:51:18.691 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 2 Activity
2019-09-19 10:51:18.698 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 3 Activity
2019-09-19 10:51:18.706 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 4 Activity
2019-09-19 10:51:18.725 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onNewIntent: 1 Activity
2019-09-19 10:51:19.226 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 1 Activity
2019-09-19 10:51:19.265 16700-16700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 5 Activity

4. singleInstance:单实例模式

很多资料都直接说这种模式的Activity 会单独占用一个任务栈,即整个系统中就这一个实例,同样也是栈内复用,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了,感觉说的很抽象,那它跟singleTask有什么区别呢?

例:有Activity 1 --> Activity 2 -> Activity 3-> Activity 4 -> Activity 5 -> Activity 1 , 其中Activity 1 设置成singleInstance属性,在Activity5中设置跳转到Activity 1, 在Activity 5中重写它的onStart()和onStop()方法,并且在从Activity 5跳转到Activity 1之后,再次点击Activity1的跳转按钮(本来默认是跳转到Activity2的),然后再按返回键,观察结果:

Logcat:

2019-09-19 11:09:33.686 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 1 Activity
2019-09-19 11:09:36.620 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 2 Activity
2019-09-19 11:09:37.246 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 3 Activity
2019-09-19 11:09:37.958 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 4 Activity
2019-09-19 11:09:38.748 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 5 Activity
2019-09-19 11:09:38.750 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onStart: 5 Activity
2019-09-19 11:09:40.490 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onNewIntent: 1 Activity
2019-09-19 11:09:40.956 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onStop: 5 Activity
2019-09-19 11:09:48.412 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onStart: 5 Activity
2019-09-19 11:09:52.911 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onStop: 5 Activity
2019-09-19 11:09:52.912 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 5 Activity
2019-09-19 11:09:53.676 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 4 Activity
2019-09-19 11:09:54.905 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 3 Activity
2019-09-19 11:09:56.857 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 2 Activity
2019-09-19 11:09:59.147 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 1 Activity

这次的Logcat有些怪异,大概说明一下:

我先是依次从Activity1 点到 Activity 5, 注意到从Activty 1到Activity2的过渡动画发生了改变,与Activity2 跳转到Activity3的动画不同,另外因为在Activity 5 新加了个 onStart() 方法,所以这次log上也有所体现:

2019-09-19 11:09:33.686 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 1 Activity
2019-09-19 11:09:36.620 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 2 Activity
2019-09-19 11:09:37.246 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 3 Activity
2019-09-19 11:09:37.958 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 4 Activity
2019-09-19 11:09:38.748 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 5 Activity
2019-09-19 11:09:38.750 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onStart: 5 Activity

当停留在Activity 5时,我再次点击跳转(默认行为是跳转到Activity1, 它的launchMode是singleInstance),注意到此时并未重新调用Activity1的onCreate()方法,而是调用了它的 onNewIntent() 方法,同时调用了Activity 5的 onStop() 方法,因为Activity 5已不再可见。

2019-09-19 11:09:40.490 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onNewIntent: 1 Activity
2019-09-19 11:09:40.956 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onStop: 5 Activity

此时到达了Activity 1界面,我在Activity1界面再次点击跳转(注意此时的默认行为是跳转到Activity 2),  但是它没有像我预想的那样跳转到Activity 2, 而是直接显示Activity 5, 所以此时显示Activity 5的onStart()方法:

2019-09-19 11:09:48.412 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onStart: 5 Activity

最后我在Activity 5界面连续点击返回键,注意到相关的Activity 也会一 一的被关闭。

2019-09-19 11:09:52.911 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onStop: 5 Activity
2019-09-19 11:09:52.912 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 5 Activity
2019-09-19 11:09:53.676 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 4 Activity
2019-09-19 11:09:54.905 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 3 Activity
2019-09-19 11:09:56.857 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 2 Activity
2019-09-19 11:09:59.147 18700-18700/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 1 Activity

总结:与singleTask不同,当从Activity 5再次点击回到Activity1的时候,Activity2,3,4,5 都不会销毁,但都会调用Activity 1的 onNewIntent()方法。

5.FLAG_ACTIVITY_CLEAR_TOP 

之所以会提到这个,是因为早期曾经在项目里看到别人用这个Flag来关闭和退出应用,且从Google的说明上看,它与singleTask mode有一定的相似性:都能清除目标Activity之上的Activity:

If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent. 

For example, consider a task consisting of the activities: A, B, C, D. If D calls startActivity() with an Intent that resolves to the component of activity B, then C and D will be finished and B receive the given Intent, resulting in the stack now being: A, B. 

The currently running instance of activity B in the above example will either receive the new intent you are starting here in its onNewIntent() method, or be itself finished and restarted with the new intent. If it has declared its launch mode to be "multiple" (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created; for all other launch modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to the current instance's onNewIntent(). 

This launch mode can also be used to good effect in conjunction with FLAG_ACTIVITY_NEW_TASK: if used to start the root activity of a task, it will bring any currently running instance of that task to the foreground, and then clear it to its root state. This is especially useful, for example, when launching an activity from the notification manager. 

See Tasks and Back Stack for more information about tasks. 

那么它与singleTask到底有何区别?还是这个例子:

例:有Activity 1 --> Activity 2 -> Activity 3-> Activity 4 -> Activity 5 -> Activity 1,将Activity1的launch mode 删除,然后在Activity5中修改其跳转到Activity1时的代码,为跳转Intent增加这个Flag:  Intent.FLAG_ACTIVITY_CLEAR_TOP :

    public void next(View view)
    {
        Intent intent = new Intent(getApplicationContext(), LaunchMode1Activity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
    }

Logcat:

2019-09-19 13:20:08.704 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 1 Activity
2019-09-19 13:20:10.749 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 2 Activity
2019-09-19 13:20:11.131 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 3 Activity
2019-09-19 13:20:11.495 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 4 Activity
2019-09-19 13:20:11.808 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 5 Activity
2019-09-19 13:20:12.657 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 2 Activity
2019-09-19 13:20:12.665 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 3 Activity
2019-09-19 13:20:12.671 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 4 Activity
2019-09-19 13:20:12.690 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 1 Activity
2019-09-19 13:20:12.725 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 1 Activity
2019-09-19 13:20:13.016 11308-11308/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 5 Activity

注意到,与上面的singleTask相同的是,都会销毁掉Activity 2 ~ Activity 5之间的所有Activity (包括Activity5), 但是Activity 1却会重新创建。那么这与Google的说明不一样呀,Google貌似说会调用 onNewIntent() 方法什么的,如何理解Google的这段说明文字?其实Google也说明了:

The currently running instance of activity B in the above example will either receive the new intent you are starting here in its onNewIntent() method, or be itself finished and restarted with the new intent.

上面示例中当前运行的Ativity B实例将在它的onNewIntent()方法中接收您从此处开始的新intent, 或者本身已被finish掉并重新使用这个intent来restart。(翻译的不好,我理解的大概如此)

If it has declared its launch mode to be "multiple" (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created; for all other launch modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to the current instance's onNewIntent(). 

如果它已将它的启动模式声明为“multiple”(默认值)并且您没有在同一意图中设置FLAG_ACTIVITY_SINGLE_TOP,那么它将被finish并重新创建; 对于所有其他启动模式或如果设置了FLAG_ACTIVITY_SINGLE_TOP,则此Intent将被传递到当前实例的onNewIntent()

大概意思就是说如果没有同时设置 FLAG_ACTIVITY_SINGLE_TOP 那么目标Activity将会被finish并重新创建,如果同时设置了 FLAG_ACTIVITY_SINGLE_TOP ,那么会将intent传递到目标Activity的  onNewIntent() 方法。那好吧,修改Activity 5 跳转到 Activity 1的代码:

    public void next(View view)
    {
        Intent intent = new Intent(getApplicationContext(), LaunchMode1Activity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        startActivity(intent);
    }

增加上面所说的那个Flag, 同样执行Activity 1 --> Activity 2 -> Activity 3-> Activity 4 -> Activity 5 -> Activity 1操作:

Logcat:

2019-09-19 13:43:06.774 12673-12673/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 1 Activity
2019-09-19 13:43:07.556 12673-12673/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 2 Activity
2019-09-19 13:43:08.042 12673-12673/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 3 Activity
2019-09-19 13:43:08.448 12673-12673/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 4 Activity
2019-09-19 13:43:08.921 12673-12673/com.yongdaimi.android.androidapitest I/xp.chen: onCreate: 5 Activity
2019-09-19 13:43:09.952 12673-12673/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 2 Activity
2019-09-19 13:43:09.961 12673-12673/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 3 Activity
2019-09-19 13:43:09.972 12673-12673/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 4 Activity
2019-09-19 13:43:09.991 12673-12673/com.yongdaimi.android.androidapitest I/xp.chen: onNewIntent: 1 Activity
2019-09-19 13:43:10.271 12673-12673/com.yongdaimi.android.androidapitest I/xp.chen: onDestroy: 5 Activity

果然,经过这种方式修改后,其展示的行为与singleTask 保持一致。

综上,可以利用singleTask和FLAG_ACTIVITY_CLEAR_TOP 的这种特性来关闭和退出应用。

参考链接:

1. Android中Activity的启动模式(LaunchMode)和使用场景

原文地址:https://www.cnblogs.com/yongdaimi/p/11548914.html