Activity的task相关 详解
task是一个具有栈结构的容器,可以放置多个Activity实例。启动一个应用,系统就会为之创建一个task,来放置根Activity;默认情况下,一个Activity启动另一个Activity时,两个Activity是放置在同一个task中的,后者被压入前者所在的task栈,当用户按下后退键,后者从task被弹出,前者又显示在幕前,特别是启动其他应用中的Activity时,两个Activity对用户来说就好像是属于同一个应用;系统task和task之间是互相独立的,当我们运行一个应用时,按下Home键回到主屏,启动另一个应用,这个过程中,之前的task被转移到后台,新的task被转移到前台,其根Activity也会显示到幕前,过了一会之后,在此按下Home键回到主屏,再选择之前的应用,之前的task会被转移到前台,系统仍然保留着 task内的所有Activity实例,而那个新的task会被转移到后台,如果这时用户再做后退等动作,就是针对该task内部进行操作了。
我们今天就讲一下和task相关的知识,主要分一下几点:
1. Activity的affinity(亲和力)
2. Intent几种常见的flags
3. <activity>与task相关属性
一、 affinity:
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。
我们会在后面进行详细讲解。
默认情况下,一个应用内的所有Activity都具有相同的affinity,都是从Application(参 考<application>的taskAffinity属性)继承而来,而Application默认的affinity 是<manifest>中的包名,我们可以为<application>设置taskAffinity属性值,这样可以应用 到<application>下的所有<activity>,也可以单独为某个Activity设置taskAffinity。 例如:在系统自带的Browser中,package为com.android.browser,但是<application>却自定义一 个taskAffinity属性值:
<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:( —> 跳转到新activity时刻发生)
当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。( appB.FirstActivity --> appA.SecondActivity )
appA中的SecondActivity配置如下:
<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跳转代码如下:
Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY");
startActivity(intent);
我们要演示几个步骤:
1. 在appB中的FirstActivity点击按钮跳转到appA中的SecondActivity;
图 1
图2
2. 按Home键回到主屏,在主选单中再次启动appB;
图 3
3. 按Home键回到主屏,在主选单中启动appA。演示过程如图所示:
图 4
我们发现在从appB跳转到appA的SecondActivity之后,SecondActivity实例好像是嵌入到了appB中,但是不影响appA的正常运行,
这种关系如下图所示:
图 5
然后我们修改一下跳转的代码:
Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
我们加上了FLAG_NEW_TASK标记,在来看一下演示结果:
1. 在appB中的FirstActivity点击按钮跳转到appA中的SecondActivity;
图 6
图 7
2. 按Home键回到主屏,在主选单中再次启动appB:
图 8
3. 按Home键回到主屏,在主选单中启动appA。演示过程如图所示:
图 9
我们看到差别了吧,当我们再次启动appB时已经看不到刚才启动的appA中的SecondActivity,而启动appA时却直接看到了,说明 这个SecondActivity实例并不在appB的task内,而是创建了一个task,这个task的affinity就是 SecondActivity默认的affinity,由于appA的SecondActivity的affinity是从Application继承而 来,所以当appA启动时会直接找到这个task,而不是创建新的task。我们看一下解析图:
图 10
2. FLAG_ACTIVITY_CLEAR_TOP:( <— 跳转回旧activity时刻发生)
当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实例如下:
图 11
从ThirdActivity跳转到SecondActivity时,跳转代码如下:
Intent intent = new Intent(this, SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
然后跳转后SecondActivity实例如下:
12
从序列号可以看到这两个实例是不同的,证明它是经过了销毁和重新的过程。
然后我们把ThirdActivity中的跳转代码添加FLAG_ACTIVITY_SINGLE_TOP标记:
Intent intent = new Intent(this, SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
两次实例均如下图所示:
图 13
如果我们不想添加FLAG_ACTIVITY_SINGLE_TOP,那么把SecondActivity的启动模式改为“standard”之外的三种即可,效果和上面一样,都不会创建新的实例。
3. FLAG_ACTIVITY_SINGLE_TOP:
当task中存在目标Activity实例并且位于栈的顶端时,不再创建一个新的,直接利用这个实例。我们在上边的例子中也有讲到。
4. FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET:(后台的task重新回到前台 时发生)
如果一个Intent中包含此属性,则它转向的那个Activity以及在那个Activity其上的所有Activity都会在task重置时被清除出task。当我们将一个后台的task重新回到前台时,系统会在特定情况下为这个动作附带一个FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记,意味着必要时重置task,这时FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET就会生效。
经过测试发现,对于一个处于后台的应用,(1)、如果在主选单点击应用,这个动作中含有FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记,(2)、 长按Home键,然后点击最近记录,这个动作不含FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记, 所以前者会清除,后者不会。
关于这个标记,可以下图示之:
图 14
这个标记对于应用存在分割点的情况会非常有用。比如我们在应用主界面要选择一个图片,然后我们启动了图片浏览界面,但是把这个应用从后台恢复到前台 时,为了避免让用户感到困惑,我们希望用户仍然看到主界面,而不是图片浏览界面,这个时候我们就要在转到图片浏览界面时的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中,我们将跳转动作做以下改动:
Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com.hk"));
startActivity(viewIntent);
进入appB时的界面:
图 15
启动web界面之后:
图 16
然后我们按Home键,是当前应用退居后台,我们回到主选单,重新启动appB,界面如下:
图 17
此时我们在主选单中启动Browser应用,出现在我们眼前的界面是这样的:
图 18
以上这种行为也证明了我们前面的论断,为了更清楚的说明问题,也为了让大家自己可以验证,下面我们要再次演示一下appB和appA的启动过程:
对于appA,在上面的基础上,不用修改其他地方,只需为SecondActivity的<activity>元素添加一个属性,如下:
<activity android:name=".SecondActivity" android:allowTaskReparenting="true">
...
</activity>
然后,在appB中的FirstActivity跳转代码改为:
Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY");
startActivity(intent);
我们启动appB,看到以下界面:
图 19
然后点击按钮,跳转到appA中的SecondActivity,界面如下:
图 20
此时appB中的FirstActivity和appA中的SecondActivity处于同一个task中taskid为28,然后我们按下Home键,在主选单中再次启动appB,我们发现appA的SecondActivity不见了,我们看到的是:
图 21
然后我们启动appA,这是我们不会看到它的FirstActivity,而是看到了它的SecondActivity:
图 22
通常两个应用分别有自己的task,它们的taskid肯定不同,但这里的SecondActivity却显示taskid与appB相同,我们想一下也许就明白了,原来它是appB迁徙过来的,再启动appA时并未生成任何新的Activity实例。这个时候如果我们按下后退键,appA就会立即退出,证明了此时appA的task里只有一个Activity实例,也就是这个SecondActivity实例。
需要注意的是,如果appB退居后台之后,没有再次启动appB,而是直接启动appA,将不会出现以上现象。
重新宿主的动作发生在appB再次启动的过程中(task 分裂)。
android:allowReparenting的效果图如下:
图 23
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>元素,如下:
<activity android:name=".FirstActivity"
android:clearTaskOnLaunch="true">
...
</activity>
FristActivity界面如下:
图 24
然后我们让FirstActivity跳转到SecondActivity,结果如下:
图 25
然后我们按Home键回到主界面,再次启动appB,我们看到以下结果:
图 26
我们看到,再次启动appB时,我们只能看到FirstActivity界面,此时在FirstActivity之上的所有Activity都已经被清除出栈。示意图如下:
图 27
4. android:finishOnTaskLaunch
这个属性和android:allowReparenting属性相似,不同之处在于allowReparenting属性是重新宿主到有共同 affinity的task中,而finishOnTaskLaunch属性是销毁实例。如果这个属性和 android:allowReparenting都设定为“true”,则这个属性胜出。
以上就是今天总结的内容,这些都是常用的知识,除此之外还有很多等着我们去探索,继续努力。
参考:
http://www.360doc.com/content/12/0118/10/8134611_180127417.shtml
Activity的task相关 详解的更多相关文章
- Activity的启动模式详解
Activity的启动模式详解 Activity有四种载入模式:standard(默认), singleTop, singleTask和 singleInstance. (1).standard(默认 ...
- 基础总结篇之三:Activity的task相关
http://blog.csdn.net/liuhe688/article/details/6761337 古人學問無遺力,少壯工夫老始成.紙上得來終覺淺,絕知此事要躬行.南宋.陸遊<冬夜讀書示 ...
- 深入浅出—Redis集群的相关详解
前言: 这篇文章主要介绍了Redis集群的相关,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值. 注意!要求使用的都是redis3.0以上的版本,因为3.0以上增加了red ...
- activity的生命周期详解
刚在看mars老师的视频,看到activity的生命周期,就看了一下,总结了一下.下面是各函数的调用时机 为了更清楚的看清楚工作的具体过程,举例如下: ,建立两个activity,一个main,一个a ...
- Activity的Launch mode详解 singleTask正解
Activity有四种加载模式:standard(默认), singleTop, singleTask和 singleInstance.以下逐一举例说明他们的区别: standard:Activity ...
- Activity的Launch mode详解,A B C D的singleTask模式
本文参考了此文http://hi.baidu.com/amauri3389/blog/item/a54475c2a4b2f040b219a86a.html 另附 android task与back s ...
- Activity的Launch mode详解 :standard(默认), singleTop, singleTask和 singleInstance
本文参考了此文http://hi.baidu.com/amauri3389/blog/item/a54475c2a4b2f040b219a86a.html 另附 android task与back s ...
- Android Activity的生命周期详解
应用程序中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应. Activity之间通过Intent进行通信.在Intent 的描述结构中,有两个最 ...
- Activity启动模式图文详解
转载自:http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0520/2897.html 英文原文:Understand Android A ...
随机推荐
- windows7使用Source insight上远程修改ubuntu共享内核源码
由于本人阅读喜欢使用source insight.前段时间接触了linux核代码,而这份代码只能放在ubuntu服务器上编译,刚开始的时候是在windows上修改,完了之后再copy到服务器上去编译, ...
- Servlet细节
Servlet细节 线程不安全的做法: * 不要在Servlet中创建成员!创建局部变量即可! * 可以创建无状态成员! * 可以创建有状态的成员,但状态必须为只读的!(不提供set方法) 1.Ser ...
- The dependency `BaiduMapKit` is not used in any concrete target
RubertdeMacBook-Pro:项目名 Rubert$ pod install Analyzing dependencies [!] The dependency `BaiduMapKit` ...
- DBA_Oracle Erp R12中文补丁安装升级(案例)
2014-07-11 Created By BaoXinjian
- animateWithDuration 动画的速度选择
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnim ...
- arm-linux-objdump
一.arm-linux-objdump常用来显示二进制文件信息,常用来查看反汇编代码二.常用选项:1.-b bfdname 指定目标码格式2.—disassemble或者-d 反汇编可执行段3.—di ...
- C++学习19 类的多继承
在前面的例子中,派生类都只有一个基类,称为单继承.除此之外,C++也支持多继承,即一个派生类可以有两个或多个基类. 多继承容易让代码逻辑复杂.思路混乱,一直备受争议,中小型项目中较少使用,后来的 Ja ...
- [ActionScript&Flex] FlashBuilder编译条件之如何屏蔽调试代码
下面讲一下在FlashBuilder中如何添加编译器参数使我们在发布的时候不编译调试代码: 首先设置编译参数 编译参数设置好后,代码我们可以这样写: public class ConditionalC ...
- poj 2632 Crashing Robots
点击打开链接 Crashing Robots Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6655 Accepted: ...
- net start mysql服务名无效
解决方案:(参考以下命令) 1.win+R键输入cmd敲回车进入dos界面: 2.输入cd d:/mysql-5.5.25/bin敲回车,发现没变化: 3.输入d:敲回车,定位到d:\mysql-5. ...