作项目到如今都一直没有理解LaunchMode有什么用,或许根本就没真正花心思去看,因此今天把这部分整理下。html
设置Activity的LaunchMode属性能够决定这个Activity是和当前Task保持关联,仍是说每次运行这个Activity是新建一个实例,仍是保持单例。java
Task和Back Stack简介android
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压入栈顶。app
以此类推,activity2转向activity3也是同样的步骤。那么当前栈顶是activity3。ide
当咱们按下手机上的返回键时,栈顶的activity3触发onPause,activity2须要从状态stop到pause,因此触发了onPause->onStart->onResume,activity3触发onStop->onDestory,由于activity3从栈顶弹出,因此触发onDestory,此时,activity2在栈顶。ui
若是继续按返回键,当前栈顶的activity弹出并被destory,直到home界面。当全部的activity都弹出了,这个task也就消亡了。this
当开始一个新的task时,前一个task被设置为后台,在后台,全部的activity都处理stop状态,可是back stack保留了全部后台activity的状态信息,只是丢失了焦点。3d
反复的在两个activity之间切换,activity会产生多个独立的实例。日志
查阅有关Activity生命周期更多说明。
两种方式设置LaunchMode属性
1. 在 manifest文件中设置
<activity android:name=".activity.ActivityA" android:launchMode="standard"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
2. 使用Intent flags设置
Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClass(ActivityA.this, ActivityB.class); startActivity(intent);
四种LaunchMode说明
standard
不作任何设置,默认模式就是standard,activity在每次start时,都会有一个新的实例被task管理。下面看下代码实例。
//ActivityA.java Intent intent = new Intent(); intent.setClass(ActivityA.this, ActivityB.class); startActivity(intent); //ActivityB.java Intent intent = new Intent(); intent.setClass(ActivityB.this, ActivityA.class); startActivity(intent);
操做1:在ActivityA(蓝)和ActivityB(绿)之间重复切换,按返回键推到home界面。
能够发现(蓝色86和绿色79的taskID)ActivityA和ActivityB都在同一个task,而且每次resume的实例都是不同的。这说明在一个activity能够有多个实例在同一个task中。
在按返回按键时,将依次弹出stack。
singleTop
和standard同样,能够屡次实例,但,若是处于当前栈顶而且接受到一个与当前activity同样类型的intent,那么不会建立一个新实例,而是触发onNewIntent()事件。
//ActivityA.java Intent intent = new Intent(); intent.setClass(ActivityA.this, ActivityA.class); startActivity(intent); @Override protected void onNewIntent(Intent intent) { logger.d("onNewIntent " + this.hashCode() + " taskID " + this.getTaskId()); super.onNewIntent(intent); }
<activity android:name=".activity.ActivityA" android:label="ActivityA"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</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实例同时存在。
<activity android:name=".activity.ActivityA" android:label="ActivityA" android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.ActivityB" android:label="ActivityB" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".activity.ActivityC" android:label="ActivityC" android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
操做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。
<activity android:name=".activity.ActivityA" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".activity.ActivityB" android:launchMode="singleInstance"> <intent-filter> <action android:name="android.intent.action.MAIN" /> </intent-filter> </activity> <activity android:name=".activity.ActivityC"> <intent-filter> <action android:name="android.intent.action.MAIN" /> </intent-filter> </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更像是代表了activity属于哪一个task,默认状况下,应用全部的activities都有相同的affinity,因此都是在相同的task中。而后你能够编辑默认的affinity。Activities定义在不一样的应用能够共享一个affinity,或者activities定义在相同的应用中能够被不一样的affinities所关联。
你能够编辑在<activity>元素中
activity的taskAffinity属性。
先看看两种不一样的状况下affinity的表现:
FLAG_ACTIVITY_NEW_TASK标记
//ActivityA.java
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClass(ActivityA.this, ActivityB.class);
startActivity(intent);
<activity android:name=".activity.ActivityA" android:taskAffinity="com.android.demo.affinity1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.ActivityB" android:taskAffinity="com.android.demo.affinity2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
操做1:不一样的affinity值,ActivityA->ActivityB

若是已经存在相同affinity,那么新activity运行在这个task中,不然,系统建立新task。
操做2:相同的affinity值,ActivityA->ActivityB
<activity android:name=".activity.ActivityA" android:taskAffinity="com.android.demo.affinity1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.ActivityB" android:taskAffinity="com.android.demo.affinity2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</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。
//Demo1
//ActivityA.java
Intent intent = new Intent();
intent.setClass(ActivityA.this, ActivityC.class);
startActivity(intent);
//ActivityC.java
tv.setText(ActivityC.this.toString());
//Demo2
//ActivityB.java
Intent intent = new Intent();
intent.setClassName("com.android.demo","com.android.demo.activity.ActivityC");
ActivityB.this.startActivity(intent);
//Demo1
<activity android:name=".activity.ActivityA">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.ActivityC" android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
//Demo2
<activity android:name=".ActivityB">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</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。
//Demo1
<activity android:name=".activity.ActivityC"
android:taskAffinity="com.android.demo.activityc"
android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
运行结果:

从结果中能够看出,Demo1和Demo2都拥有ActivityC,也就是说有2个Task里存在ActivityC,分别被Demo1和Demo2所使用。
操做3:将ActivityC和ActivityB的taskAffinity都设为"com.android.demo.activityc"。
运行Demo2,转向到ActivityC,在ActivityC中打印信息,返回到HOME界面,运行Demo1。
//Demo2
<activity android:name=".ActivityB" android:taskAffinity="com.android.demo.activityc">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//Demo1
<activity android:name=".activity.ActivityC"
android:taskAffinity="com.android.demo.activityc"
android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
运行结果:

和操做1相反,再进入Demo2时看到是ActivityC,进入Demo1都是看到ActivityA。
写到最后愈来愈崩溃了,若是有什么地方写的不对或不清楚请指明。
转帖请说明原文出处:http://www.cnblogs.com/SteveMing/archive/2012/04/24/2459575.html