Android中Activity的启动模式

从为什么使用启动模式、启动模式是什么、怎么使用来介绍。

理解几个概念:Application,Task和Process

  • Application

应用,由四大组件组成的,在app安装时,系统会读取manifest的信息,将所有的组件解析出来,以便在运行时对组件进行实例化和调度。这一点可以从清单文件中看的更直接:

1
2
3
4
5
6
<application android:label="@string/app_name">
<activity > </activity>
<receiver />
<provider />
<service />
</application>

  • Process

进程,在默认情况下,一个应用程序的所有组件运行在同一个进程中。但是这种情况也有例外,即,应用程序中的不同组件可以运行在不同的进程中。只需要在manifest中用process属性指定组件所运行的进程的名字。如下所示:

1
2
3
<activity android:name=".MyActivity" android:label="@string/app_nam"
android:process=":remote">
</activity>

  • Task

是一种用来放置Activity实例的容器,是一种先进后出的栈结构,主要有2个基本操作:压栈和出栈,其所存放的Activity是不支持重新排序的,只能根据压栈和出栈操作更改Activity的顺序。启动一个Application的时候,系统会为它默认创建一个对应的Task,用来放置根Activity。默认启动Activity会放在同一个Task中,新启动的Activity会被压入启动它的那个Activity的栈中,并且显示它。当用户按下回退键时,这个Activity就会被弹出栈,按下Home键回到桌面,再启动另一个应用,这时候之前那个Task就被移到后台,成为后台任务栈,而刚启动的那个Task就被调到前台,成为前台任务栈,手机页面显示的就是前台任务栈中的栈顶元素。

为什么要使用启动模式

  • 在开发过程中遇到奇怪的或者不是自己想要的页面跳转,这时候启动模式可以派上用场
  • 有时候我们的App需要生成给其他App调用的Activity,例如浏览器应用,照相机应用:Task是可以跨进程和跨应用的
  • 解决生成重复页面等等Bug
  • 任务栈过深的时候,避免一直按返回键也退不回想要的页面

四种启动模式

standard

  • 标准模式,这是Activity的默认模式,所有的Activity遵循元素“先进后出”的特性。
  • 每次启动一个Activity都会创建一个新的实例,不管这个实例是否已经存在。
  • 被创建的实例的生命周期符合典型情况下Activity的生命周期,它的onCreate、onStart、onResume都会被调用。
  • 在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的Activity所在的栈中。
  • 如果使用ApplicationContext去启动一个标准模式的Activity就会报错,这是由于非Activity类型的Context没有任务栈,被启动的Activity无处安放。解决这个问题的办法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会为它创建一个新的任务栈,这个时候待启动的Activity实际上是以singTask模式启动的。

singleTop

  • 栈顶复用模式
  • 如果待启动的Activity已经位于栈顶,则不会被重新创建,它的onCreate、onStart不会被调用,但是会调用它的onNewIntent方法。
  • 如果待启动的Activity实例已经存在但不是位于栈顶,仍然会重新创建。
  • 只有在栈顶才会复用,不在栈顶则与标准模式类似。

singleTask

  • 栈内复用模式
  • 这是一种单实例模式
  • 在这种模式下,只要这个Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,和singleTop一样,也会调用onNewIntent。
  • 当一个设置为singleTask模式的Activity被启动后,例如Activity A ,会有以下四种情况:

    ①:A想要的任务栈不存在:创建新的任务栈,并创建A的实例后把A放入栈中;

    ②:A想要的任务栈已经存在,但这个栈中不存在A的实例:创建A的实例,并把A压入栈中;

    ③:A想要的任务栈已经存在,而且这个栈中也存在A的实例,但是A不位于栈顶:此时会将A之上的Activity压出栈(清楚出去,销毁实例),使A位于栈顶。这时,不会执行onCreate、onStart方法,而是执行onNewIntent方法。

    ④:A想要的任务栈已经存在,而且这个栈中也存在A的实例,并且A位于栈顶:此时仅仅执行onNewIntent方法。

  • 什么是A想要的任务栈

默认情况下,所有Activity所需的任务栈的名字为应用的包名。如果要为某个Activity指定单独 的任务栈,就要设置TaskAffinity这个属性参数。TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。

singleInstance

  • 单实例模式
  • 加强的singleTask,它具有singleTask模式具有的所有特性,加强之处在于:只能单独位于一个任务栈中。
  • 比如Activity A是singleInstance模式,启动A后,系统会为它创建一个新的任务栈,然后A独自在这个心的任务栈,由于栈内复用的特性,后续的请求均不会创建新的Activity,除非这个独特的任务栈被系统销毁了。

如何指定Activity的启动模式

  • 第一种方式:在清单文件中配置
1
2
3
4
<activity
android:name=".TestActivity"
android:launchMode="standard或者singleInstance或者singleTask或者singleTop">
</activity>
  • 第二种方式:在Intent中设置标志位
1
2
3
4
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
intent.addFlags(intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
  • 当两种同时存在时,以第二种也就是在Intent中设置标志位为准,它的优先级更高。
  • 第一种无法直接为Activity指定FLAG_ACTIVITY_CLEAR_TOP标识,第二种无法为Activity指定singleInstance模式

参考