首页 > 其他分享 >基础总结篇之三:Activity的task相关

基础总结篇之三:Activity的task相关

时间:2023-09-12 14:35:54浏览次数:37  
标签:task SecondActivity 之三 ACTIVITY 实例 Intent Activity


古人學問無遺力,少壯工夫老始成。紙上得來終覺淺,絕知此事要躬行。南宋.陸遊《冬夜讀書示子聿(yù)》

软件行业也是一样,多少前辈不遗余力的奋斗才出现了软件行业的繁荣的景象,其中已有不少成为大师级人物。今天我们站在伟人的肩膀上,自然会有不少的优势,但不要忘了,要在对技术的认知方面有所提升,仍需我们去实践,去实践。

今天我们来讲一下Activity的task相关内容。

上次我们讲到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:

affinity对于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属性值:


1. <application   android:name="Browser"  
2. android:label="@string/application_name"  
3. android:icon="@drawable/ic_launcher_browser"  
4. android:backupAgent=".BrowserBackupAgent"  
5. 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配置如下:


1. <activity android:name=".SecondActivity">  
2. <intent-filter>  
3. <action android:name="android.intent.action.APP_A_SECOND_ACTIVITY" />  
4. <category android:name="android.intent.category.DEFAULT" />  
5. </intent-filter>  
6. </activity>

然后,在appB中的FirstActivity跳转代码如下:

1. Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY");  
2. startActivity(intent);

我们要演示几个步骤:1.在appB中的FirstActivity点击按钮跳转到appA中的SecondActivity;2.按Home键回到主屏,在主选单中再次启动appB;3.按Home键回到主屏,在主选单中启动appA。演示过程如图所示:

基础总结篇之三:Activity的task相关_java

再次启动appB应用:

基础总结篇之三:Activity的task相关_启动模式_02

启动appA应用:

基础总结篇之三:Activity的task相关_启动模式_03

我们发现在从appB跳转到appA的SecondActivity之后,SecondActivity实例好像是嵌入到了appB中,但是不影响appA的正常运行,这种关系如下图所示:

基础总结篇之三:Activity的task相关_android_04

然后我们修改一下跳转的代码:

1. Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY");  
2. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
3. startActivity(intent);

我们加上了FLAG_NEW_TASK标记,在来看一下演示结果:

基础总结篇之三:Activity的task相关_启动模式_05

再次启动appB:

基础总结篇之三:Activity的task相关_启动模式_06

启动appA:

基础总结篇之三:Activity的task相关_启动模式_07

我们看到差别了吧,当我们再次启动appB时已经看不到刚才启动的appA中的SecondActivity,而启动appA时却直接看到了,说明这个SecondActivity实例并不在appB的task内,而是创建了一个task,这个task的affinity就是SecondActivity默认的affinity,由于appA的SecondActivity的affinity是从Application继承而来,所以当appA启动时会直接找到这个task,而不是创建新的task。我们看一下解析图:

基础总结篇之三:Activity的task相关_android_08

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实例如下:

基础总结篇之三:Activity的task相关_android_09

从ThirdActivity跳转到SecondActivity时,跳转代码如下:

1. Intent intent = new Intent(this, SecondActivity.class);  
2. intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
3. startActivity(intent);

然后跳转后SecondActivity实例如下:

基础总结篇之三:Activity的task相关_启动模式_10

从序列号可以看到这两个实例是不同的,证明它是经过了销毁和重新的过程。

然后我们把ThirdActivity中的跳转代码添加FLAG_ACTIVITY_SINGLE_TOP标记:

1. Intent intent = new Intent(this, SecondActivity.class);  
2. intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);  
3. startActivity(intent);

两次实例均如下图所示:

基础总结篇之三:Activity的task相关_android_11

如果我们不想添加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标记,所以前者会清除,后者不会。关于这个标记,可以下图示之:

基础总结篇之三:Activity的task相关_java_12

这个标记对于应用存在分割点的情况会非常有用。比如我们在应用主界面要选择一个图片,然后我们启动了图片浏览界面,但是把这个应用从后台恢复到前台时,为了避免让用户感到困惑,我们希望用户仍然看到主界面,而不是图片浏览界面,这个时候我们就要在转到图片浏览界面时的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中,我们将跳转动作做以下改动:

1. Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com.hk"));  
2. startActivity(viewIntent);

进入appB时的界面:

基础总结篇之三:Activity的task相关_java_13

基础总结篇之三:Activity的task相关_启动模式_14

启动过程:

对于appA,在上面的基础上,不用修改其他地方,只需为SecondActivity的<activity>元素添加一个属性,如下:

1. <activity android:name=".SecondActivity" android:allowTaskReparenting="true">  
2. ...           
3. </activity>

然后,在appB中的FirstActivity跳转代码改为:

1. Intent intent = new Intent("android.intent.action.APP_A_SECOND_ACTIVITY");  
2. startActivity(intent);

我们启动appB,看到一下界面:

基础总结篇之三:Activity的task相关_启动模式_15

通常两个应用分别有自己的task,它们的taskid肯定不同,但这里的SecondActivity却显示taskid与appB相同,我们想一下也许就明白了,原来它是appB迁徙过来的,再启动appA时并未生成任何新的Activity实例。这个时候如果我们按下后退键,appA就会立即退出,证明了此时appA的task里只有一个Activity实例,也就是这个SecondActivity实例。

需要注意的是,如果appB退居后台之后,没有再次启动appB,而是直接启动appA,将不会出现以上现象。重新宿主的动作发生在appB再次启动的过程中。

android:allowReparenting的效果图如下:

基础总结篇之三:Activity的task相关_启动模式_16

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>元素,如下:

1. <activity android:name=".FirstActivity"  
2. android:clearTaskOnLaunch="true">  
3.         ...      
4. </activity>

FristActivity界面如下:

基础总结篇之三:Activity的task相关_启动模式_17

我们看到,再次启动appB时,我们只能看到FirstActivity界面,此时在FirstActivity之上的所有Activity都已经被清除出栈。示意图如下:

基础总结篇之三:Activity的task相关_java_18

这个属性和android:allowReparenting属性相似,不同之处在于allowReparenting属性是重新宿主到有共同affinity的task中,而finishOnTaskLaunch属性是销毁实例。如果这个属性和android:allowReparenting都设定为“true”,则这个属性胜出。

以上就是今天总结的内容,这些都是常用的知识,除此之外还有很多等着我们去探索,继续努力。

标签:task,SecondActivity,之三,ACTIVITY,实例,Intent,Activity
From: https://blog.51cto.com/u_14523369/7445254

相关文章

  • 软件设计模式系列之三———工厂方法模式
    1模式的定义工厂方法模式是一种常见的设计模式,属于创建型设计模式之一,它在软件工程中用于对象的创建。该模式的主要思想是将对象的创建过程抽象化,将具体对象的实例化延迟到子类中完成,以便在不同情况下可以创建不同类型的对象,而客户端代码不需要知道实际创建的对象类型。2举例......
  • onBackPressed 设置返回值,onActivityResult resultcode一直是0
    activity1:Intentintent2=newIntent(this,checkactivity.class);startActivityForResult(intent2,1000);activity2:@OverridepublicvoidonBackPressed(){super.onBackPressed();Intentintent=newIntent();setResult(result,in......
  • 使用Windows Task Scheduler进行OneDrive强制同步
    前言OneDrive的同步策略非常反人类:它允许用户同步文件,但仅限于其划定范围的特定文件夹/文件类型。这意味着用户不能对任意文件夹进行同步,简直是难以想象!图1OneDrive对备份文件的选项仅限于几个文件夹内,体现了老牌科技企业在教育用户如何使用计算机上的良苦用心StrawmanSolut......
  • 【230911-3】☆反炮兵听声辨位之三点定敌炮(鼠标左键点击模拟敌发炮)位置
    【说明】使用鼠标点击左键模拟敌炮发射,两条双曲线交汇位置即敌炮所在位置。【图示】【代码】<!DOCTYPEhtml><htmllang="utf-8"><metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/><head><title>反炮兵听声辨位之三点定敌炮(鼠标左键点击模拟敌发炮)......
  • Strimzi从入门到精通系列之三:部署Kafka Connect
    Strimzi从入门到精通系列之三:部署KafkaConnect一、概述二、将KafkaConnect部署到Kubernetes集群三、KafkaConnect配置四、为多个实例配置KafkaConnect五、添加连接器六、自动使用连接器插件构建新的容器映像七、使用KafkaConnect基础镜像中的连接器插件构建新的容器镜......
  • 获取在电脑上的apk的appPackage和appActivity
    如果开发有给apk,那么可以在电脑上通过以下方法获得appPackage和appActivitycmd进入appt路径  然后输入指令(apk路径不要含有中文)aaptdumpbadgingapk路径(D:\**\**\**.apk)以上输出结果过多,我们可以按以下指令获取我们需要的数据获取appPackageaaptdumpbadgin......
  • Android系统启动流程、APP启动流程、activity启动流程
    Android系统启动流程打开电源时,先启动BootLoader→使用BootLoader加载Linux内核→启动init进程→启动zygote进程-(zygote调用system_server进程)→拉起packageManagerService和AMS(ActivityManagerService)→AMS启动Launcher应用。 APP启动流程1,桌面程序点击app图标......
  • 入门篇-其之三-基本数据类型及其转换
    文章结构如下目录整数类型浮点类型字符类型布尔类型数字的进制表示(了解)原码、反码、补码(了解)自动类型提升强制类型转换Java的数据类型分为基本数据类型和引用数据类型,具体分类如下图:对于初学者而言,认为字符串类型String也属于基本数据类型,事实上String属于类,即引用数据类型。......
  • zone.js由入门到放弃之三——zone.js 源码分析【setTimeout篇】
    Delegate是个好东西,看看孙啸达同学对ZoneDelegate的介绍吧,这是他关于zone.js系列文章的第三篇~zone.js系列往期文章zone.js由入门到放弃之一——通过一场游戏认识zone.jszone.js由入门到放弃之二——zone.jsAPI大练兵zone.js源码分析接下来的全是干货,从头到尾,一干到底一点前置:Zon......
  • System.Threading.Tasks.Extensions介绍
    System.Threading.Tasks.Extensions是一个用于扩展.NET中任务(Task)的库,它提供了一些额外的功能,特别是在异步编程方面。这个库引入了一些新的方法和功能,包括:ConfigureAwait:它引入了ConfigureAwait方法,允许你在任务之间配置不同的上下文(例如,同步上下文或异步上下文),以便更好地......