个人先做个关于launchMode和taskAffinity简单总结。转载的文章贴在后面。
<activity
android:name=".activity.ActivityMain"
android:label="@string/title_activity_activity_main"
android:launchMode="singleTask"
android:taskAffinity="com.figo.study">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
设置launchMode可以定义Actitity被创建的时候是单例还是每次New一个出来,LaunchMode总共有4种
Standard默认值,每次会重新创建一个Activity
SingleTop,如果某个Activity已经在Task Stack栈顶的话,那么就不会重新new一个activity出来了,否则会重新new一个出来
SingleTask,如果需要跳转到某个Activity已经在某个Task Stack之中,比如Activity A,B,C,D,现在D在栈顶,要从D跳转到B,B的launchMode为SingleTask,那么这个时候这个Task Stack将会只剩下A,B。B在栈顶。
SingleInstance,比如Activity A已经打开现在所在的TaskId=100,这个时候A要跳转到Activity B,B的LaunchMode为SingleInstance,那么B首先会检查是否已经有B了,没有的话会重新创建一个Stack,让自己独占这个Stack。
设置taskAffinity的目的是让当前activity是否和其他activity在同一个任务栈中,如果都没有设置的话,taskAffinity默认值是包名即com.xxx.xxx。taskAffinity往往和allowTaskReparenting(是否允许重新选择宿主)一起使用,比如某个App A的activity a的allowTaskReparenting为true,而且taskAffinity和另外一个App B的launch activty的taskAffinity一样的话,打开App B,你会发现其实这个时候展示的是activity a。
Activity中方法getTaskId()获得activity所在的taskid,isTaskRoot()获取当前activity是否是在栈底。
这方面看过很多篇文章,目前这篇文章是我看过关于介绍LaunchMode和taskAffinity比较好的文章,看的这篇也是转载,我继续转载过来,以便将来查询,转载地址:
http://blog.csdn.net/wangshione/article/details/8491098
另外关于taskAffinity文章
http://blog.csdn.net/wangshione/article/details/8491249
以下为转载内容。原文有些错误,我纠正了一些。
设置Activity的LaunchMode属性可以决定这个Activity是和当前Task保持关联,还是说每次运行这个Activity是新建一个实例,还是保持单例。
Task和Back Stack简介
task是一组Activities的集合,一组Activities被Stack(back stack)所管理。
在一个应用中,有3个activities,分别是activity1,activity2,activity3,首先activity1被start,此时,如果应用没有创建task则创建,并把activity1压入栈顶,activity1触发onCreate->onStart->onResume。
接着activity1转向到activity2时,activity1先触发onPause,activity2触发onCreate->onStart->onResume,然后activity1触发onPause->onStop,activity2压入栈顶。
以此类推,activity2转向activity3也是一样的步骤。那么当前栈顶是activity3。
当我们按下手机上的返回键时,栈顶的activity3触发onPause,activity2需要从状态stop到pause,所以触发了onPause->onStart->onResume,activity3触发onStop->onDestory,因为activity3从栈顶弹出,所以触发onDestory,此时,activity2在栈顶。
如果继续按返回键,当前栈顶的activity弹出并被destory,直到home界面。当所有的activity都弹出了,这个task也就消亡了。
当开始一个新的task时,前一个task被设置为后台,在后台,所有的activity都处理stop状态,但是back stack保留了所有后台activity的状态信息,只是丢失了焦点。
反复的在两个activity之间切换,activity会产生多个独立的实例。
查阅有关Activity生命周期更多说明。
两种方式设置LaunchMode属性
1. 在 manifest文件中设置
1 | <activity android:name=".activity.ActivityA" |
2 | android:launchMode="standard"> |
3 | <intent-filter> |
4 | <action android:name="android.intent.action.MAIN" /> |
5 | <category android:name="android.intent.category.LAUNCHER" /> |
6 | </intent-filter> |
7 | </activity> |
2. 使用Intent flags设置
1 | Intent intent = new Intent(); |
2 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
3 | intent.setClass(ActivityA.this, ActivityB.class); |
4 | startActivity(intent); |
四种LaunchMode说明
standard
不做任何设置,默认模式就是standard,activity在每次start时,都会有一个新的实例被task管理。下面看下代码实例。
1 | //ActivityA.java |
2 | Intent intent = new Intent(); |
3 | intent.setClass(ActivityA.this, ActivityB.class); |
4 | startActivity(intent); |
5 | |
6 | //ActivityB.java |
7 | Intent intent = new Intent(); |
8 | intent.setClass(ActivityB.this, ActivityA.class); |
9 | startActivity(intent); |
操作1:在ActivityA(蓝)和ActivityB(绿)之间重复切换,按返回键推到home界面。
可以发现(蓝色86和绿色79的taskID)ActivityA和ActivityB都在同一个task,并且每次resume的实例都是不一样的。这说明在一个activity可以有多个实例在同一个task中。
在按返回按键时,将依次弹出stack。
singleTop
和standard一样,可以多次实例,但,如果处于当前栈顶并且接受到一个与当前activity一样类型的intent,那么不会创建一个新实例,而是触发onNewIntent()事件。
01 | <code>//ActivityA.java |
02 | Intent intent = new Intent(); |
03 | intent.setClass(ActivityA.this, ActivityA.class); |
04 | startActivity(intent); |
05 | |
06 | @Override |
07 | protected void onNewIntent(Intent intent) { |
08 | logger.d("onNewIntent " + this.hashCode() + " taskID " |
09 | + this.getTaskId()); |
10 | super.onNewIntent(intent); |
11 | } |
1 | <activity android:name=".activity.ActivityA" android:label="ActivityA" |
2 | android:launchMode="singleTop"> |
3 | <intent-filter> |
4 | <action android:name="android.intent.action.MAIN" /> |
5 | <category android:name="android.intent.category.LAUNCHER" /> |
6 | </intent-filter> |
7 | </activity> |
操作1:点击ActivityA上的按钮
发现当点击按钮是ActivityA->onPause->onNewIntent->onResume,没有新建新的实例(蓝62)。
这个模式在这个场景下比较有用,比如:如果有一个其他的应用想启动你的Activity(launch mode为singleTop),而你当前的Activity正好在栈顶,那么就会调用到onNewIntent方法。原文贴上:If an instance of the activity already exists at the top of the current task, the system routes the intent to that instance through a call to its onNewIntent() method。
singleTask
系统会创建一个新task(如果没有启动应用)和一个activity新实例在新task根部,然后,如果activity实例已经存在单独的task中,系统会调用已经存在activity的 onNewIntent()方法,而不是存在新实例,仅有一个activity实例同时存在。
01 | <code><activity android:name=".activity.ActivityA" android:label="ActivityA"android:launchMode="standard"> |
02 | <intent-filter> |
03 | <action android:name="android.intent.action.MAIN" /> |
04 | <category android:name="android.intent.category.LAUNCHER" /> |
05 | </intent-filter> |
06 | </activity> |
07 | <activity android:name=".activity.ActivityB" android:label="ActivityB"android:launchMode="singleTask"> |
08 | <intent-filter> |
09 | <action android:name="android.intent.action.MAIN" /> |
10 | </intent-filter> |
11 | </activity> |
12 | <activity android:name=".activity.ActivityC" android:label="ActivityC"android:launchMode="standard"> |
13 | <intent-filter> |
14 | <action android:name="android.intent.action.MAIN" /> |
15 | </intent-filter> |
16 | </activity></code> |
操作1:ActivityA->ActivityB->ActivityC->ActivityA->ActivityB->ActivityC
可以看到,当再次进入ActivityB时,没有onCreate,而是onNewIntent(绿55)。
这里我们也可以发现一个现象,当在调用到ActivityB的onNewIntent时,之前的ActivityA和ActivityC都调用了onDestory。也就是说,系统发现栈中存在ActivityB的实例时,ActivityA和ActivityB都弹栈了。
列出Log日志(这里设ActivityA的LaunchMode为singleTask),ActivityB和ActivityC都在onNewIntent前后调用了onDestory。
singleInstance
和singleTask相似,除了系统不会让其他的activities运行在所有持有的task实例中,这个activity是独立的,并且task中的成员只有它,任何其他activities运行这个activity都将打开一个独立的task。
01 | <activity android:name=".activity.ActivityA" android:launchMode="singleTask"> |
02 | <intent-filter> |
03 | <action android:name="android.intent.action.MAIN" /> |
04 | <category android:name="android.intent.category.LAUNCHER" /> |
05 | </intent-filter> |
06 | </activity> |
07 | <activity android:name=".activity.ActivityB" android:launchMode="singleInstance"> |
08 | <intent-filter> |
09 | <action android:name="android.intent.action.MAIN" /> |
10 | </intent-filter> |
11 | </activity> |
12 | <activity android:name=".activity.ActivityC"> |
13 | <intent-filter> |
14 | <action android:name="android.intent.action.MAIN" /> |
15 | </intent-filter> |
16 | </activity> |
操作1:ActivityA->ActivityB->ActivityA
可以发现,两个Activity是在不同的Task中,其次,当调用到onNewIntent时,ActivityB没有被Destory,互不干涉。
操作2:ActivityA->ActivityB->ActivityC,按返回键
图解:
刚进入应用,创建TaskA,ActivityA为栈顶,从ActivityA到ActivityB,ActivityB进入TaskB(如果再次进入ActivityB,则不创建Task,调用onNewIntent),此时TaskB中的ActivityB为栈顶,从ActitivyB到ActivityC,ActivityC为栈顶。
一直按返回键,先从TaskA中依次将Activity弹出,然后再从TaskB中将ActiviyB弹出。ActiviyC->ActivityA->ActivityB。
这里分析一个问题,浏览器的LaunchMode为singleTask,所以如果当你点击一个连接下载文件时(由一个activity来处理下载,launchmode为standard),如果再次进入浏览器,那么下载页面就被Destory了,那么这里我们可以把下载页面LaunchMode设置为singleInstance可以解决这个问题。
Affinity定义Affinity更像是表明了activity属于哪个task,默认情况下,应用所有的activities都有相同的affinity,所以都是在相同的task中。然后你可以编辑默认的affinity。Activities定义在不同的应用可以共享一个affinity,或者activities定义在相同的应用中可以被不同的affinities所关联。
你可以编辑在<activity>元素中activity的taskAffinity属性。
先看看两种不同的情况下affinity的表现:
- 当运行一个activity包含了FLAG_ACTIVITY_NEW_TASK标记
1 //ActivityA.java 2 Intent intent = new Intent(); 3 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 4 intent.setClass(ActivityA.this, ActivityB.class); 5 startActivity(intent); 01 <activity android:name=".activity.ActivityA"android:taskAffinity="com.android.demo.affinity1"> 02 <intent-filter> 03 <action android:name="android.intent.action.MAIN" /> 04 <category android:name="android.intent.category.LAUNCHER" /> 05 </intent-filter> 06 </activity> 07 <activity android:name=".activity.ActivityB"android:taskAffinity="com.android.demo.affinity2"> 08 <intent-filter> 09 <action android:name="android.intent.action.MAIN" /> 10 </intent-filter> 11 </activity> 操作1:不同的affinity值,ActivityA->ActivityB
如果已经存在相同affinity,那么新activity运行在这个task中,否则,系统创建新task。
操作2:相同的affinity值,ActivityA->ActivityB
01 <activity android:name=".activity.ActivityA"android:taskAffinity="com.android.demo.affinity1"> 02 <intent-filter> 03 <action android:name="android.intent.action.MAIN" /> 04 <category android:name="android.intent.category.LAUNCHER" /> 05 </intent-filter> 06 </activity> 07 <activity android:name=".activity.ActivityB"android:taskAffinity="com.android.demo.affinity1"> 08 <intent-filter> 09 <action android:name="android.intent.action.MAIN" /> 10 </intent-filter> 11 </activity> 可以看出ActivityA和ActivityB都运行在同一个task中。
- 当Activity的allowTaskReparenting的属性设为'true'
使用来表示是否允许activity重新附属其他Task,还是举例说明吧。
有两个应用,Demo1和Demo2,Demo1中有2个Activity(ActivityA,ActivityC),ActivityA可以转向到ActivityC,Demo2中有一个Activity(ActivityB),也可以转向到ActivityC。
操作1:设置ActivityC的allowTaskReparenting属性为true。
运行Demo2,转向到ActivityC,在ActivityC中打印信息,返回到HOME界面,运行Demo1。
01 //Demo1 02 //ActivityA.java 03 Intent intent = new Intent(); 04 intent.setClass(ActivityA.this, ActivityC.class); 05 startActivity(intent); 06
07 //ActivityC.java 08 tv.setText(ActivityC.this.toString()); 09
10 //Demo2 11 //ActivityB.java 12 Intent intent = new Intent(); 13 intent.setClassName("com.android.demo","com.android.demo.activity.ActivityC"); 14 ActivityB.this.startActivity(intent); 01 //Demo1 02 <activity android:name=".activity.ActivityA"> 03 <intent-filter> 04 <action android:name="android.intent.action.MAIN" /> 05 <category android:name="android.intent.category.LAUNCHER" /> 06 </intent-filter> 07 </activity> 08 <activity android:name=".activity.ActivityC" android:allowTaskReparenting="true"> 09 <intent-filter> 10 <action android:name="android.intent.action.MAIN" /> 11 </intent-filter> 12 </activity> 13
14
15 //Demo2 16 <activity android:name=".ActivityB"> 17 <intent-filter> 18 <action android:name="android.intent.action.MAIN" /> 19 <category android:name="android.intent.category.LAUNCHER" /> 20 </intent-filter> 21 </activity> 运行结果:(黄色Demo1,绿色Demo2)
ActivityB转向到ActivityC,此时ActivityC就关联到Demo2的Task中,TaskID都为231。在运行Demo1时,看到是ActivityC而不是ActivityA。当再次进入Demo2时就看不到ActivityC了。
操作2:将ActivityC的taskAffinity设置为"com.Android.demo.activityc"。
运行Demo2,转向到ActivityC,在ActivityC中打印信息,返回到HOME界面,运行Demo1。
1 //Demo1 2 <activity android:name=".activity.ActivityC" 3 android:taskAffinity="com.android.demo.activityc" 4 android:allowTaskReparenting="true"> 5 <intent-filter> 6 <action android:name="android.intent.action.MAIN" /> 7 </intent-filter> 8 </activity> 运行结果:
从结果中可以看出,Demo1和Demo2都拥有ActivityC,也就是说有2个Task里存在ActivityC,分别被Demo1和Demo2所使用。
操作3:将ActivityC和ActivityB的taskAffinity都设为"com.android.demo.activityc"。
运行Demo2,转向到ActivityC,在ActivityC中打印信息,返回到HOME界面,运行Demo1。
01 //Demo2 02 <activity android:name=".ActivityB" android:taskAffinity="com.android.demo.activityc"> 03 <intent-filter> 04 <action android:name="android.intent.action.MAIN" /> 05 <category android:name="android.intent.category.LAUNCHER" /> 06 </intent-filter> 07 </activity> 08
09 //Demo1 10 <activity android:name=".activity.ActivityC" 11 android:taskAffinity="com.android.demo.activityc" 12 android:allowTaskReparenting="true"> 13 <intent-filter> 14 <action android:name="android.intent.action.MAIN" /> 15 </intent-filter> 16 </activity> 运行结果:
和操作1相反,再进入Demo2时看到是ActivityC,进入Demo1都是看到ActivityA。