Android 的一个特点就是 application A 的 activity 能够启动 application B 的 activity,尽管 A 和 B 是毫无干系的,而在用户看来,两个场景紧密联系,视觉上两者构成了一个总体。Android 就是把这种误觉定义为 Task,它既不是 class,也不是 AndroidMainifest.xml 中的一个元素。从表现上看 Task 就像是一个 stack,一个一个的 activity 是构成 stack 的元素,作着入栈 (push) 和出栈 (pop-up)这样简单重复性的劳动。
默认的规则老是知足大多数的应用场景,可是也总会有一些例外打破习觉得常的惯例。Task 的默认规则一样并不是牢不可破,修改的方法仍是有的。借助 Intent 中的 flag 和 AndroidMainifest.xml 中 activity 元素的属性,就能够控制到 Task 里 Activity 的关联关系和行为。
在 android.content.Intent 中一共定义了20种不一样的 flag,其中和 Task 紧密关联的有四种:
1.FLAG_ACTIVITY_NEW_TASK
2.FLAG_ACTIVITY_CLEAR_TOP
3.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
4.FLAG_ACTIVITY_SINGLE_TOP
在使用这四个 flag 时,一个 Intent 能够设置一个 flag,也能够选择若干个进行组合。
默认状况下,经过 startActivity() 启动一个新的 Activity,新的 Activity 将会和调用者在同一个 stack 中。可是,若是在传递给 startActivity() 的 Intent 对象里包含了 FLAG_ACTION_NEW_TASK,状况将发生变化,–系统将为新的 Activity “寻找”一个不一样于调用者的 Task。不过要找的 Task 是否是必定就是 NEW 呢?若是是第一次执行,则这个设想成立,若是说不是,也就是说已经有一个包含此 Activity 的Task 存在,则不会再启动 Activity。
若是 flag 是 FLAG_ACTIVITY_CLEAR_TOP,同时当前的 Task 里已经有了这个 Activity,那么情形又将不同。Android 不但不会启动新的 Activity 实例,并且还会将 Task 里 该 Activity 之上的全部 Activity 一概结束掉,而后将 Intent 发给这个已存在的 Activity。Activity 收到 Intent 以后,能够在 onNewIntent() 里作下一步的处理,也能够自行结束而后从新建立本身。若是 Activity 在 AndroidMainifest.xml 里将启动模式设置成”multiple”,– 默认模式,而且 Intent 里也没有设置 FLAG_ACTIVITY_SINGLE_TOP,那么它将选择后者。不然,它将选择前者。FLAG_ACTIVITY_CLEAR_TOP 还能够和 FLAG_ACTION_NEW_TASK 配合使用。
若是 flag 设置的是 FLAG_ACTIVITY_SINGLE_TOP,则意味着若是 Activity 已是运行在 Task 的 top,则该 Activity 将不会再被启动。html
设置intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); java
下面是关于setFlags的一下属性的解释:android
1、Activity和Task(栈)的关系
Task就像一个容器,而Activity就至关与填充这个容器的东西,第一个东西(Activity)则会处于最下面,最后添加的东西(Activity)则会在最低端。从Task中取出东西(Activity)则是从最顶端取出,也就是说最早取出的是最后添加的东西(Activity),一次类推,最后取出的是第一次添加的Activity,而Activity在Task中的顺序是能够控制的,那则在Activity跳转时用到Intent Flag
2、界面跳转和服务的启动都会用到Intent,如今介绍Intent Flag是关于Activity的跳转
Intent intent = new Intent(this,xxx.class);
//若是activity在task存在,拿到最顶端,不会启动新的Activity
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
//若是activity在task存在,将Activity之上的全部Activity结束掉
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//默认的跳转类型,将Activity放到一个新的Task中
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//若是Activity已经运行到了Task,再次跳转不会在运行这个Activity
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);web
来自:http://www.anddevs.com/?p=118浏览器
1.Activity的affinity(亲和力)app
2.Intent几种常见的flags测试
3.<activity>与task相关属性this
affinity:google
task对于Activity来讲就好像它的身份证同样,能够告诉所在的task,本身属于这个task中的一员;拥有相同affinity的多个Activity理论同属于一个task,task自身的affinity决定于根Activity的affinity值。affinity在什么场合应用呢?1.根据affinity从新为Activity选择宿主task(与allowTaskReparenting属性配合工做);2.启动一个Activity过程当中Intent使用了FLAG_ACTIVITY_NEW_TASK标记,根据affinity查找或建立一个新的具备对应affinity的task。咱们会在后面进行详细讲解。spa
默认状况下,一个应用内的全部Activity都具备相同的affinity,都是从Application(参考<application>的taskAffinity属性)继承而来,而Application默认的affinity是<manifest>中的包名,咱们能够为<application>设置taskAffinity属性值,这样能够应用到<application>下的全部<activity>,也能够单独为某个Activity设置taskAffinity。例如:在系统自带的Browser中,package为com.android.browser,可是<application>却自定义一个taskAffinity属性值:
[html] view plaincopy
<application android:name="Browser"
android:label="@string/application_name"
android:icon="@drawable/ic_launcher_browser"
android:backupAgent=".BrowserBackupAgent"
android:taskAffinity="android.task.browser" >
Intent几种常见的flags:
在android.content.Intent中定义了若干个flags,其中最重要的有如下几个:
1.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最好的宿主。下面咱们会经过实例进行演示这个特性:
咱们新建两个项目,分别命名为appA和appB,而且分别建立FirstActivity和SecondActivity,咱们准备让appB中的FirstActivity跳转到appA的SecondActivity。appA中的SecondActivity配置以下:
[html] view plaincopy
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.APP_A_SECOND_ACTIVITY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
而后,在appB中的FirstActivity跳转代码以下:
[java] view plaincopy
Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY");
startActivity(intent);
咱们要演示几个步骤:1.在appB中的FirstActivity点击按钮跳转到appA中的SecondActivity;2.按Home键回到主屏,在主选单中再次启动appB;3.按Home键回到主屏,在主选单中启动appA。演示过程如图所示:
再次启动appB应用:
启动appA应用:
咱们发如今从appB跳转到appA的SecondActivity以后,SecondActivity实例好像是嵌入到了appB中,可是不影响appA的正常运行,这种关系以下图所示:
而后咱们修改一下跳转的代码:
[java] view plaincopy
Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
咱们加上了FLAG_NEW_TASK标记,在来看一下演示结果:
再次启动appB:
启动appA:
咱们看到差异了吧,当咱们再次启动appB时已经看不到刚才启动的appA中的SecondActivity,而启动appA时却直接看到了,说明这个SecondActivity实例并不在appB的task内,而是建立了一个task,这个task的affinity就是SecondActivity默认的affinity,因为appA的SecondActivity的affinity是从Application继承而来,因此当appA启动时会直接找到这个task,而不是建立新的task。咱们看一下解析图:
2.FLAG_ACTIVITY_CLEAR_TOP:当Intent对象包含这个标记时,若是在栈中发现存在Activity实例,则清空这个实例之上的Activity,使其处于栈顶。例如:咱们的FirstActivity跳转到SecondActivity,SecondActivity跳转到ThirdActivity,而ThirdActivity又跳到SecondActivity,那么ThirdActivity实例将被弹出栈,使SecondActivity处于栈顶,显示到幕前,栈内只剩下FirstActivity和SecondActivity。这个SecondActivity既能够在onNewIntent()中接收到传来的Intent,也能够把本身销毁以后从新启动来接受这个Intent。在使用默认的“standard”启动模式下,若是没有在Intent使用到FLAG_ACTIVITY_SINGLE_TOP标记,那么它将关闭后重建,若是使用了这个FLAG_ACTIVITY_SINGLE_TOP标记,则会使用已存在的实例;对于其余启动模式,无需再使用FLAG_ACTIVITY_SINGLE_TOP,它都将使用已存在的实例,Intent会被传递到这个实例的onNewIntent()中。
下面咱们来验证一下这个过程:
首先,Activity启动模式都按照默认值“standard”。从FirstActivity跳转到SecondActivity,SecondActivity实例以下:
从ThirdActivity跳转到SecondActivity时,跳转代码以下:
[java] view plaincopy
Intent intent = new Intent(this, SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
而后跳转后SecondActivity实例以下:
从序列号能够看到这两个实例是不一样的,证实它是通过了销毁和从新的过程。
而后咱们把ThirdActivity中的跳转代码添加FLAG_ACTIVITY_SINGLE_TOP标记:
[java] view plaincopy
Intent intent = new Intent(this, SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
两次实例均以下图所示:
若是咱们不想添加FLAG_ACTIVITY_SINGLE_TOP,那么把SecondActivity的启动模式改成“standard”以外的三种便可,效果和上面同样,都不会建立新的实例。
3.FLAG_ACTIVITY_SINGLE_TOP:当task中存在目标Activity实例而且位于栈的顶端时,再也不建立一个新的,直接利用这个实例。咱们在上边的例子中也有讲到。
4.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET:若是一个Intent中包含此属性,则它转向的那个Activity以及在那个Activity其上的全部Activity都会在task重置时被清除出task。当咱们将一个后台的task从新回到前台时,系统会在特定状况下为这个动做附带一个FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记,意味着必要时重置task,这时FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET就会生效。通过测试发现,对于一个处于后台的应用,若是在主选单点击应用,这个动做中含有FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记,长按Home键,而后点击最近记录,这个动做不含FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记,因此前者会清除,后者不会。关于这个标记,能够下图示之:
这个标记对于应用存在分割点的状况会很是有用。好比咱们在应用主界面要选择一个图片,而后咱们启动了图片浏览界面,可是把这个应用从后台恢复到前台时,为了不让用户感到困惑,咱们但愿用户仍然看到主界面,而不是图片浏览界面,这个时候咱们就要在转到图片浏览界面时的Intent中加入此标记。
5.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED:这个标记在如下状况下会生效:1.启动Activity时建立新的task来放置Activity实例;2.已存在的task被放置于前台。系统会根据affinity对指定的task进行重置操做,task会压入某些Activity实例或移除某些Activity实例。咱们结合上面的CLEAR_WHEN_TASK_RESET能够加深理解。
<activity>的task相关属性
在<activity>中定义了几个常见的task相关属性,它们分别表明了task内部不一样的行为特征,咱们就来逐个介绍一下:
1.android:allowTaskReparenting
这个属性用来标记一个Activity实例在当前应用退居后台后,是否能从启动它的那个task移动到有共同affinity的task,“true”表示能够移动,“false”表示它必须呆在当前应用的task中,默认值为false。若是一个这个Activity的<activity>元素没有设定此属性,设定在<application>上的此属性会对此Activity起做用。例如在一个应用中要查看一个web页面,在启动系统浏览器Activity后,这个Activity实例和当前应用处于同一个task,当咱们的应用退居后台以后用户再次从主选单中启动应用,此时这个Activity实例将会从新宿主到Browser应用的task内,在咱们的应用中将不会再看到这个Activity实例,而若是此时启动Browser应用,就会发现,第一个界面就是咱们刚才打开的web页面,证实了这个Activity实例确实是宿主到了Browser应用的task内。咱们就来结合实例演示一下这个过程:
首先,在appB的FirstActivity中,咱们将跳转动做作如下改动:
[java] view plaincopy
Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com.hk"));
startActivity(viewIntent);
进入appB时的界面:
启动web界面以后:
而后咱们按Home键,是当前应用退居后台,咱们回到主选单,从新启动appB,界面以下:
此时咱们在主选单中启动Browser应用,出如今咱们眼前的界面是这样的:
以上这种行为也证实了咱们前面的论断,为了更清楚的说明问题,也为了让你们本身能够验证,下面咱们要再次演示一下appB和appA的启动过程:
对于appA,在上面的基础上,不用修改其余地方,只需为SecondActivity的<activity>元素添加一个属性,以下:
[html] view plaincopy
<activity android:name=".SecondActivity" android:allowTaskReparenting="true">
...
</activity>
而后,在appB中的FirstActivity跳转代码改成:
[java] view plaincopy
Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY");
startActivity(intent);
咱们启动appB,看到一下界面:
而后点击按钮,跳转到appA中的SecondActivity,界面以下:
此时appB中的FirstActivity和appA中的SecondActivity处于同一个task中taskid为28,而后咱们按下Home键,在主选单中再次启动appB,咱们发现appA的SecondActivity不见了,咱们看到的是:
而后咱们启动appA,这是咱们不会看到它的FirstActivity,而是看到了它的SecondActivity:
一般两个应用分别有本身的task,它们的taskid确定不一样,但这里的SecondActivity却显示taskid与appB相同,咱们想一下也许就明白了,原来它是appB迁徙过来的,再启动appA时并未生成任何新的Activity实例。这个时候若是咱们按下后退键,appA就会当即退出,证实了此时appA的task里只有一个Activity实例,也就是这个SecondActivity实例。
须要注意的是,若是appB退居后台以后,没有再次启动appB,而是直接启动appA,将不会出现以上现象。从新宿主的动做发生在appB再次启动的过程当中。
android:allowReparenting的效果图以下:
2.android:alwaysRetainTaskState
这个属性用来标记应用的task是否保持原来的状态,“true”表示老是保持,“false”表示不可以保证,默认为“false”。此属性只对task的根Activity起做用,其余的Activity都会被忽略。
默认状况下,若是一个应用在后台呆的过久例如30分钟,用户从主选单再次选择该应用时,系统就会对该应用的task进行清理,除了根Activity,其余Activity都会被清除出栈,可是若是在根Activity中设置了此属性以后,用户再次启动应用时,仍然能够看到上一次操做的界面。
这个属性对于一些应用很是有用,例如Browser应用程序,有不少状态,好比打开不少的tab,用户不想丢失这些状态,使用这个属性就极为恰当。
3.android:clearTaskOnLaunch
这个属性用来标记是否从task清除除根Activity以外的全部的Activity,“true”表示清除,“false”表示不清除,默认为“false”。一样,这个属性也只对根Activity起做用,其余的Activity都会被忽略。
若是设置了这个属性为“true”,每次用户从新启动这个应用时,都只会看到根Activity,task中的其余Activity都会被清除出栈。若是咱们的应用中引用到了其余应用的Activity,这些Activity设置了allowTaskReparenting属性为“true”,则它们会被从新宿主到有共同affinity的task中。
无图无真相,咱们就来以实例演示一下这个过程,咱们首先修改appB的根Activity的<activity>元素,以下:
[html] view plaincopy
<activity android:name=".FirstActivity"
android:clearTaskOnLaunch="true">
...
</activity>
FristActivity界面以下:
而后咱们让FirstActivity跳转到SecondActivity,结果以下:
而后咱们按Home键回到主界面,再次启动appB,咱们看到如下结果:
咱们看到,再次启动appB时,咱们只能看到FirstActivity界面,此时在FirstActivity之上的全部Activity都已经被清除出栈。示意图以下:
4.android:finishOnTaskLaunch
这个属性和android:allowReparenting属性类似,不一样之处在于allowReparenting属性是从新宿主到有共同affinity的task中,而finishOnTaskLaunch属性是销毁实例。若是这个属性和android:allowReparenting都设定为“true”,则这个属性胜出。
以上就是今天总结的内容,这些都是经常使用的知识,除此以外还有不少等着咱们去探索,继续努力。