Activity的LaunchMode
Tasks和Back Stack
- 一个Task表示与用户的一次交互,包含交互过程中的多个Activity实例。当用户通过点击桌面上的Launcher图标打开一个app时,则启动一个新的Task开始与用户交互,当用户点击Home键时,当前Task则被转入后台,而当用户再次点击桌面上相同的Launcher图标时则将刚才转入后台的Task转入前台与用户交互。
- 每个Task使用一个Back Stack来管理这些Activity实例,栈顶是当前与用户交互的Activity,由当前Activity启动另一个Activity后,则新启动的Activity压入栈顶。如果用户使用Back键返回上一个Activity,则弹出栈顶Activity。
- 一个Task中可能涉及多个App中的Activity,因为一次交互可能会涉及多个App的协作,一个App也可能使用多个Task与用户交互。一个Task与App的一次运行并不是一一对应的关系。
Activity的几种LaunchMode
由一个Activity启动另一个Activity通常使用Intent,然后调用startActivity(Intent)方法。
假设需要启动的是ActivityA,对于ActivityA有以下几种启动模式:
- standard模式
默认的模式,即总是为ActivityA创建一个新的实例并压入当前Task的Back Stack栈顶。当前Task的Back Stack中可能存在多个此ActivityA的实例。
- singleTop模式
若当前Task的Back Stack栈顶不是ActivityA的实例,则创建一个新的ActivityA实例并压入栈顶,若当前Task的Back Stack栈顶已经是ActivityA的实例则不再创建新的实例,而是直接启动栈顶ActivityA实例并通过onNewIntent方法传入Intent对象(onNewIntent方法会在onResume方法之前被调用)。
- singleTask模式
若某个Task(若该Task位于后台,则转入前台)的Back Stack栈已经包含ActivityA的实例,则不再创建新的实例,而是直接启动已经存在的ActivityA的实例并通过onNewIntent方法传入Intent对象,此种情况下,若已经存在的ActivityA实例不在Back Stack的栈顶,则弹出ActivityA实例之上的所有Activity使得ActivityA的实例位于栈顶。
若没有Task包含ActivityA的实例,则创建一个新的ActivityA实例,并启动一个新的Task(实际上不一定总是启动新的Task)来保存新创建的ActivityA实例。
使用singleTask模式启动ActivityA,则始终只有一个ActivityA的实例。
- singleInstance模式
与singleTask模式基本一样,在没有Task包含ActicityA的实例情况下,会启动一个新的Task来保存新创建的AcitivityA实例,不同的是该新启动的Task将始终只包含ActivityA的唯一实例,不会再包含其他Activity实例。
使用singleInstance模式启动ActivityA,则始终只有一个ActivityA实例,并且此ActivityA实例独占一个Task。
关于singleTask模式的说明
上面说在没有Task包含ActivityA实例的情况下,会启动一个新的Task来保存新创建的ActivityA实例,实际上并不一定如此。Activity还有一个taskAffinity属性,用来声明该Activity倾向于保存在哪个Task中,属于同一个app的Activity默认拥有相同的taskAffinity。
在singleTask模式没有Task包含ActivityA实例的情况下,系统会先寻找是否存在与ActivityA拥有相同taskAffinity的ActivityB实例,若找到了则将新创建的ActivityA实例保存在ActivityB实例所在的Task中,找不到的情况下才会启动一个新的Task。因此,如果当前启动ActivityA的Activity和ActivityA属于同一个app,则新创建的ActivityA实例仍保存在当前Task。
Intent启动Activity的Flag
除了在AndroidManifest.xml文件中配置Activity的LaunchMode,也可以在使用Intent启动Activity时设置Flag来达到相同的效果,并且当这两种方式设置的启动模式有冲突时,系统优先采用Intent设置Flag的方式。
- FLAG_ACTIVITY_NEW_TASK
此Flag与singleTask模式基本相似(而且启动新Task也需要参考taskAffinity属性),所不同的是使用FLAG_ACTIVITY_NEW_TASK不会弹出ActivityA实例之上的所有其他Acticity实例。
此Flag通常配合其他Flag一起使用,配合FLAG_ACTIVITY_CLEAR_TOP时则与singleTask模式相同。
- FLAG_ACTIVITY_SINGLE_TOP
此Flag与singleTop模式相同
- FLAG_ACTIVITY_CLEAR_TOP
使用此Flag来启动ActivityA实例时,若当前Task中已经存在ActivityA的实例,则不再创建ActivityA的实例,并弹出ActivityA实例之上的其他Activity使得ActivityA实例位于栈顶。若当前Task不存在ActivityA的实例则创建新的实例并入栈。
此Flag通常配合FLAG_ACTIVITY_NEW_TASK一起使用,当配合FLAG_ACTIVITY_NEW_TASK一起使用时则与singTask模式相同。
- FLAG_ACTIVITY_MULTI_TASK
使用此Flag来启动ActivityA实例时,总是启动一个新的Task来包含创建的ActivityA实例。
需要注意的是单独使用此Flag没有效果,此Flag必须与FLAG_ACTIVITY_NEW_TASK或者FLAG_ACTIVITY_NEW_DOCUMENT配合使用。
- FLAG_ACTIVITY_NEW_DOCUMENT
此Flag通常配合FLAG_ACTIVITY_MULTI_TASK一起使用,这样在多个ActivityA实例中打开多个document时,对于每个document都会启动一个Task,并且在Overview Screen中每个document都对应一个Task缩略图。
document表示app希望同时维护多个实例的组件,比如:文本文件(同时编辑多个doc文件)、网页(多Tab形式)、邮件等。
对于这些document组件,通常每次创建一个Activity实例去维护时都希望在一个新的Task中保存创建的Activity实例,以避免编辑状态被其他Activity实例所破坏。此时就可以使用FLAG_ACTIVITY_NEW_DOCUMENT配合FLAG_ACTIVITY_MULTI_TASK来达到效果。当然还有其他的documentLaunchMode来用于控制如何创建Activity实例维护这些document,不作展开。
当FLAG_ACTIVITY_MULTI_TASK配合FLAG_ACTIVITY_NEW_TASK或者FLAG_ACTIVITY_NEW_DOCUMENT使用时都会每次启动新的Task来保存创建的Activity实例。不同的是与前者配合时,虽然启动了多个Task,但在Overview Screen中只有一个Task缩略图;而与后者配合使用时,启动的每个Task在Overview Screen中都有一个对应的Task缩略图,这样用户可以方便的点击Task缩略图来选择继续维护某个document的状态。