上边的第3点是恒定不变的,第1和第2点,则可以在清单文件中通过设置属性android:launchMode的值即设置activity的启动模式来进行改变。 activity有四种启动模式:standard、singleTop、singleTask 和 singleInstance,如不配置,默认是standard的。
在Activity类中,有一个getTaskId()方法,我们可以通过它获取当前activity所在的任务栈的ID,getTaskId()方法的API注释为:Return the identifier of the task this activity is in.This identifier will remain the same for the lifetime of the activity.
下边用一个demo来说明activity的四种启动模式: 共3个Activity:MainActivity、FirstActivity 和 SecondActivity,它们的布局文件都一样,只在最底部放一个button用来跳转:
- <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.hwgt.launchmode.MainActivity" >
- <Button
- android:id="@+id/bt_Jump"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="13dp"
- android:layout_centerHorizontal="true"
- android:text="跳转到 FirstActivity"
- android:textSize="15dp"/>
- </RelativeLayout></span></span>
- <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">//MainActivity:
- Intent intent = new Intent(MainActivity.this, FirstActivity.class);
- MainActivity.this.startActivity(intent);
- //FirstActivity:
- Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
- FirstActivity.this.startActivity(intent);
- //SecondActivity:
- Intent intent = new Intent(SecondActivity.this, MainActivity.class);
- SecondActivity.this.startActivity(intent);</span></span>
standard:每一次都重新创建实例在3个Activity的启动模式都为standard时,log打印情况如下:getTaskId()的值保持不变,并且每一次跳转都在创建新的实例现在更改SecondActivity中的跳转逻辑为:
- <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">Intent intent = new Intent(SecondActivity.this, SecondActivity.class);
- SecondActivity.this.startActivity(intent);</span></span>
singleTop:即使栈内有,只要栈顶没有就重新创建,栈顶有就不创建现在更改SecondActivity的启动模式为singleTop,则log打印情况如下:getTaskId()的值保持不变,栈顶有SecondActivity的实例时,就直接复用,并且on
singleTask :栈内唯一,且会将位于该实例之上的activity全部销毁现在更改SecondActivity中的跳转逻辑为:
- <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">Intent intent = new Intent(SecondActivity.this, FirstActivity.class);
- SecondActivity.this.startActivity(intent);</span></span>
singleInstance:创建一个新的Task来存放activity如果一个activity的启动模式被设置为singleInstance,那么启动时,会为该activity单独创建一个Task。现在将SecondActivity的启动模式改为singleInstance,MainActivity和FirstActivity的启动模式改为standard,三个activity中的跳转逻辑改为:
- <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">//MainActivity:
- Intent intent = new Intent(MainActivity.this, FirstActivity.class);
- MainActivity.this.startActivity(intent);
- //FirstActivity:
- Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
- FirstActivity.this.startActivity(intent);
- //SecondActivity:
- Intent intent = new Intent(SecondActivity.this, MainActivity.class);
- SecondActivity.this.startActivity(intent);</span></span>
总结一下:activity的四种启动模式解决了什么问题呢?答案是,在Task遵循后进先出的规则(即后打开的activity会处于Task的顶部,当点击返回键时,后打开的activity首先被清除出Task)的前提下,1、定义了要不要重新创建一个Task来存储新打开的activity。2、定义了新打开一个activity时,需不需要再创建一个实例(即使已经存在)。3、定义了新打开一个activity之后,任务栈Task里边activity实例的堆叠顺序,这将直接决定连续点击返回键时的退出顺序。那么,activity的四种启动模式没有解决的有什么问题呢?1、如果要用不同于 打开新activity的activity所在的那个Task 的Task来存储新打开的activity,那么,该用哪个Task?用已经存在的,还是重新创建一个?如果用已经存在的,那么用哪个?依据是什么?2、比如有一个activity A,我们已经在清单文件中规定了,它的启动模式为standard,目前任务栈中的堆叠顺序为:ABCD,现在的需求是,从D跳转到A,并且跳转到A之后,任务栈中的堆叠顺序为:A,即只留下A,这又怎么实现呢?当然,直接把BCD finish掉也可以,但一定会遇到其他的只靠启动模式不好解决的问题,即启动模式有些时候不够灵活。
由于activity的四种启动模式没有解决的这些问题,我们引入了以下两个概念:1、affinity 一个activity的affinity 用来标识它属于哪个Task,可以在清单文件中设置taskAffinity的值来改变,理论上拥有相同affinity 的activity同属于一个Task,如果不进行设置的话,都从application的affinity 继承而来,而application的affinity 默认为清单文件中的包名。2、intent 的 flag就是在用Intent开启一个Activity时,在Intent中加入flag标志。如果同时设置了要打开的activity的启动模式,则flag的优先级更高。两种方式的差别在于,前者在于描述自己,声明自己应该以何种方式被加载,而后者则是主动声明要以何种方式加载一个Activity。最常用的几个flag为:FLAG_ACTIVITY_NEW_TASK:(下边文字摘自网络,谢谢作者!)当Intent对象包含这个标记时,系统会寻找或创建一个新的task来放置目标Activity,寻找时依据目标Activity的taskAffinity属性进行匹配,如果找到一个task的taskAffinity与之相同,就将目标Activity压入此task中,如果查找无果,则创建一个新的task,并将该task的taskAffinity设置为目标Activity的taskActivity,将目标Activity放置于此task。注意,如果同一个应用中Activity的taskAffinity都使用默认值或都设置相同值时,应用内的Activity之间的跳转使用这个标记是没有意义的,因为当前应用task就是目标Activity最好的宿主。
FLAG_ACTIVITY_SINGLE_TOP
这个FLAG就相当于加载模式中的singletop,比如说原来栈中情况是A,B,C,D在D中启动D,栈中的情况还是A,B,C,D
FLAG_ACTIVITY_CLEAR_TOP
这个FLAG就相当于加载模式中的SingleTask,这种FLAG启动的Activity会把要启动的Activity之上的Activity全部弹出栈空间。类如:原来栈中的情况是A,B,C,D这个时候从D中跳转到B,这个时候栈中的情况就是A,B了
FLAG_ACTIVITY_BROUGHT_TO_FRONT
这个网上很多人是这样写的。如果activity在task存在,拿到最顶端,不会启动新的Activity。这个有可能会误导大家! 他这个FLAG其实是这个意思! 比方说我现在有A,在A中启动B,此时在A中Intent中加上这个标记。此时B就是以 FLAG_ACTIVITY_BROUGHT_TO_FRONT 这个启动的,此时在B中再启动C,D(正常启动C,D),如果这个时候在D中再启动B,这个时候最后的栈的情况是 A,C,D,B. 特别注意的是,我上面说的网上人描述的这个FLAG,会很容易让人误解成这样,A,B,C,D都是标准加载,然后我在D中启动A,这个intent加上FLAG_ACTIVITY_BROUGHT_TO_FRONT ,就会误认为变成B,C,D,A!!其实不是,这个时候应该是A,B,C,D,A.不信的人大家试试看。不过下面这个标记和这个标记就会让大家明白了!
FLAG_ACTIVITY_REORDER_TO_FRONT
就按在 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT 最后说的,如果在A,B,C,D正常启动的话,不管B有没有用FLAG_ACTIVITY_BROUGHT_TO_FRONT启动,此时在D中启动B的话,还是会变成A,C,D,B的。
FLAG_ACTIVITY_NO_HISTORY
用这个标记顾名思义! 意思就是说用这个FLAG启动的Activity,一旦推出,他就不会存在于栈中,比方说!原来是A,B,C 这个时候再C中以这个FLAG启动D的 , D再启动E,这个时候栈中情况为A,B,C,E。