Android - Activity的启动模式

android 应用程序中,一般都会发生activity的跳转和返回键的点击操作,而这就会涉及到activity启动模式问题。
首先引入任务栈Task的概念,(本篇文章不过多解释Task和Back Stack,没太多影响)Task可以理解为是一个容器,启动一个应用,系统就会创建一个Task用来存放主activity,
1、在默认情况下,以后新打开的activity都会放在同一个Task中,
2、即使Task里边已经有了某个activity的实例,在每一次跳转到该activity时,都会再创建一次。
3、Task遵循后进先出的规则,即后打开的activity会处于Task的顶部,当点击返回键时,后打开的activity首先被清除出Task。

上边的第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用来跳转:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <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"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     tools:context="com.hwgt.launchmode.MainActivity" >  
  6.     <Button   
  7.         android:id="@+id/bt_Jump"  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10.         android:layout_alignParentBottom="true"  
  11.         android:layout_marginBottom="13dp"  
  12.         android:layout_centerHorizontal="true"  
  13.         android:text="跳转到 FirstActivity"  
  14.         android:textSize="15dp"/>  
  15. </RelativeLayout></span></span>  
3个Activity中逻辑都一样:
第一、处理button的点击事件:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">//MainActivity:  
  2. Intent intent = new Intent(MainActivity.this, FirstActivity.class);  
  3. MainActivity.this.startActivity(intent);  
  4. //FirstActivity:  
  5. Intent intent = new Intent(FirstActivity.this, SecondActivity.class);  
  6. FirstActivity.this.startActivity(intent);  
  7. //SecondActivity:  
  8. Intent intent = new Intent(SecondActivity.this, MainActivity.class);  
  9. SecondActivity.this.startActivity(intent);</span></span>  
第二、onCreate()方法中打印 Log.d("HWGT", "onCreate----"+getTaskId()+"----"+this.toString());
onNewIntent()方法中打印 Log.d("HWGT", "onNewIntent----"+getTaskId()+"----"+this.toString());

standard:每一次都重新创建实例
3个Activity的启动模式都为standard时,log打印情况如下:
Android - Activity的启动模式 - H , W , G , T ... .. - H , W , G , T ... ..
getTaskId()的值保持不变,并且每一次跳转都在创建新的实例
现在更改SecondActivity中的跳转逻辑为:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <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);  
  2. SecondActivity.this.startActivity(intent);</span></span>  
log打印情况如下:
Android - Activity的启动模式 - H , W , G , T ... .. - H , W , G , T ... ..
依然是 getTaskId()的值保持不变,并且每一次跳转都在创建新的实例

singleTop:即使栈内有,只要栈顶没有就重新创建,栈顶有就不创建
现在更改SecondActivity的启动模式为singleTop,则log打印情况如下
Android - Activity的启动模式 - H , W , G , T ... .. - H , W , G , T ... ..
getTaskId()的值保持不变,栈顶有SecondActivity的实例时,就直接复用,并且onNewIntent()方法得到执行

singleTask :栈内唯一,且会将位于该实例之上的activity全部销毁
现在更改SecondActivity中的跳转逻辑为:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <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);  
  2. SecondActivity.this.startActivity(intent);</span></span>  
并且更改FirstActivity的启动模式为singleTask ,则 log打印情况如下:
Android - Activity的启动模式 - H , W , G , T ... .. - H , W , G , T ... ..
 可以看到,在从SecondActivity跳到FirstActivity时,SecondActivity被销毁了。

singleInstance:创建一个新的Task来存放activity
如果一个activity的启动模式被设置为singleInstance,那么启动时,会为该activity单独创建一个Task。
现在将SecondActivity的启动模式改为singleInstance,MainActivity和FirstActivity的启动模式改为standard,三个activity中的跳转逻辑改为:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="background-color: rgb(255, 255, 255);"><span style="font-family:Microsoft YaHei;font-size:14px;">//MainActivity:  
  2. Intent intent = new Intent(MainActivity.this, FirstActivity.class);  
  3. MainActivity.this.startActivity(intent);  
  4. //FirstActivity:  
  5. Intent intent = new Intent(FirstActivity.this, SecondActivity.class);  
  6. FirstActivity.this.startActivity(intent);  
  7. //SecondActivity:  
  8. Intent intent = new Intent(SecondActivity.this, MainActivity.class);  
  9. SecondActivity.this.startActivity(intent);</span></span>  
则 log打印情况如下:
Android - Activity的启动模式 - H , W , G , T ... .. - H , W , G , T ... ..
并且在第一次跳转到SecondActivity时,查看打开的应用列表可以发现,名为launchmode的应用图标出现了两个,这两个图标分别对应着id为44和45的Task,随便点击一个进入到launchmode应用,这个时候可以发现,点击跳转或返回键的时候,activity的清除顺序已经由对应的Task所存储activity实例的顺序决定了,singleInstance这种启动模式适用于多个应用共享一个activity的情况。

总结一下:
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。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值