Android的启动模式

来源:互联网 发布:淘宝朵以专卖店 编辑:程序博客网 时间:2024/06/06 13:10

Android的启动模式

多任务的概念

任务是一个有机整体,当用户开始新任务或通过“主页”按钮转到主屏幕时,可以移动到“后台”。 尽管在后台时,该任务中的所有 Activity 全部停止,但是任务的返回栈仍旧不变,也就是说,当另一个任务发生时,该任务仅仅失去焦点而已,如图 2 中所示。然后,任务可以返回到“前台”,用户就能够回到离开时的状态。 例如,假设当前任务(任务 A)的堆栈中有三个 Activity,即当前 Activity 下方还有两个 Activity。 用户先按“主页”按钮,然后从应用启动器启动新应用。 显示主屏幕时,任务 A 进入后台。新应用启动时,系统会使用自己的 Activity 堆栈为该应用启动一个任务(任务 B)。与该应用交互之后,用户再次返回主屏幕并选择最初启动任务 A 的应用。现在,任务 A 出现在前台,其堆栈中的所有三个 Activity 保持不变,而位于堆栈顶部的 Activity 则会恢复执行。 此时,用户还可以通过转到主屏幕并选择启动该任务的应用图标(或者,通过从概览屏幕选择该应用的任务)切换回任务 B。这是 Android系统中的一个多任务示例。

image

注:后台可以同时运行多个任务。但是,如果用户同时运行多个后台任务,则系统可能会开始销毁后台 Activity,以回收内存资源,从而导致 Activity 状态丢失。系统停止您的一个 Activity 时(例如,新 Activity 启动或任务转到前台),如果系统需要回收系统内存资源,则可能会完全销毁该 Activity。 发生这种情况时,有关该 Activity 状态的信息将会丢失。如果发生这种情况,系统仍会知道该 Activity 存在于返回栈中,但是当该 Activity 被置于堆栈顶部时,系统一定会重建 Activity(而不是恢复 Activity)。 为了避免用户的工作丢失,您应主动通过在 Activity 中实现 onSaveInstanceState() 回调方法来保留工作。

定义启动模式

使用清单文件

在清单文件中声明 Activity 时,您可以使用 元素的 launchMode 属性指定 Activity 应该如何与任务关联。分配给 launchMode 属性的启动模式共有四种:
1.standard:标准模式,也是系统的默认模式。系统在启动 Activity 的任务中创建 Activity 的新实例并向其传送Intent。Activity可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。在这种模式下,谁启动这个Activity,那么这个Activity就运行在 启动它的那个Activity所在的任务中(即所在的栈)。被创建的实例的生命周期与正常生命周期一致,它的onCreate,onStart,onResume均会被依次调用。

例子:MainActivity,secondActivity的启动模式为standard

启动 MainActivity后启动secondActivity,可以看到secondActivity与MainActivity的任务栈一致。
image

按两次back键退出程序
image

2.singleTop:栈顶复用模式。如果当前任务的顶部已存在 Activity 的一个实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建 Activity 的新实例,onCreate,onStart不会被调用, onPause,onNewIntent,onResume依次调用。Activity可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例(但前提是位于返回栈顶部的 Activity 并不是 Activity 的现有实例)。

例子:启动MainActivity,在MainActivity内启动MainActivity,然后按back键会退出,MainActivity的启动模式为singleTop。
image

例如,假设任务的返回栈包含根 Activity A 以及 Activity B、C 和位于顶部的 D(堆栈是 A-B-C-D;D 位于顶部)。收到针对 D 类 Activity 的 Intent。如果 D 具有默认的 “standard” 启动模式,则会启动该类的新实例,且堆栈会变成 A-B-C-D-D。但是,如果 D 的启动模式是 “singleTop”,则 D 的现有实例会通过 onNewIntent() 接收 Intent,因为它位于堆栈的顶部;而堆栈仍为 A-B-C-D。但是,如果收到针对 B 类 Activity 的 Intent,则会向堆栈添加B的新实例,即便其启动模式为 “singleTop” 也是如此。

注:为某个 Activity 创建新实例时,用户可以按“返回”按钮返回到前一个 Activity。 但是,当 Activity 的现有实例处理新 Intent 时,则在新 Intent 到达 onNewIntent() 之前,用户无法按“返回”按钮返回到 Activity 的状态。

3.singleTask:栈内复用模式。系统创建新任务(如果存在所需的任务栈,不需要创建)并实例化位于新任务底部的 Activity。但是,如果该 Activity 的一个实例已存在于一个单独的任务中,则系统会通过调用现有实例的onNewIntent()方法向其传送Intent,而不是创建新实例。一次只能存在 Activity 的一个实例。

(在singleTask模式下,TaskAffinity不设置的话,默认情况下的任务栈为应用的包名,不是跟启动它的Activity一样的任务栈。

MainActivity的启动模式为standard,secondActivity和ThirdActivity的启动模式为singleTask, SecondActivity设置了TaskAffinity,ThirdActivity未设置。

依次启动MainActivity,SecondActivity,ThirdActivity,可以看到ThirdActivity与MainActivity的任务栈一致为68,SecondActivity的 任务栈为69
image

无论 Activity是在新任务中启动,还是在与启动 Activity相同的任务中启动,用户按“返回”按钮始终会转到前一个Activity。但是,如果启动指定 singleTask启动模式的Activity,则当某后台任务中存在该Activity的实例时,整个任务都会转移到前台。此时,返回栈包括上移到堆栈顶部的任务中的所有 Activity。
image
显示如何将启动模式为“singleTask”的 Activity 添加到返回栈。 如果 Activity 已经是某个拥有自己的返回栈的后台任务的一部分,则整个返回栈也会上移到当前任务的顶部。

例子1:SecondActivity的启动模式为singleTask,ThirdActivity的启动模式为standard

启动SecondActivity后启动ThirdActivity,ThirdACtivity的任务栈与secondActivity相同
image

再在ThirdActivity启动SecondActivity,可以看到现在在任务栈中ThirdActivity被销毁了(被 onDestroy),singleTask具有clearTop的效果。

image

例子2:MainActivity的启动模式为standard,secondActivity的启动模式为singleTask,并设置taskAffinity与MainActivity不同

启动MainActivity后,在MainActivity内启动secondActivity,可以看到两个的任务栈不同
image

例子3:MainActivity,SecondActivity的启动模式为standard,ThirdActivity,FourActivity,FiveActivity的启动模式为singleTask,并且设置ThirdActivity,FourActivity,FiveActivity的TaskAffinity一样。

依次启动MainActivity,SecondActivity, ThirdActivity,FourActivity,FiveActivity。可以看到MainActivity,SecondActivity的任务栈为89,ThirdActivity,FourActivity,FiveActivity的任务栈为90。此时前台任务栈为90,后台任务栈为89。此时按任务键 回到SecondActivity,此时前台任务栈为89,后台任务栈为90.前台Activity为SecondActivity。
image
image
image
image
image

从SecondActivity里启动 FourActivityActivity。可以看到FiveActivity被销毁,ThirdActivity没有信息,可以得出ThirdActivity跟着任务转移到前台。此时返回栈从下而上依次为MainActivity,SecondActivity,ThirdActivity, FourActivity
image

按四次back键退出
image

4.singleInstance:单实例模式。与 “singleTask” 相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中。该 Activity 始终是其任务唯一仅有的成员;由此 Activity 启动的任何Activity均在其他的任务中打开(如果此Activity是程序的入口,那么由此 Activity启动的那个Activity将会在新创建的任务栈里)。

例子:SecondActivity的启动模式为singleInstance,MainActivity,ThirdActivity的启动模式为standard

启动 MainActivity后再启动SecondActivity,可以看到MainActivity的任务栈为53,SecondActivity的任务栈为54

image

从SecondActivity启动ThirdActivity,可以看到ThirdActivity的任务栈为53,和MainActivity相同
image

从ThirdActivity启动SecondActivity,可以看到secondActivity没有再被创建,只执行onNewIntent,onRestart,onStart,onResume
image

此时按back键回到ThirdActivity,SecondActivity被销毁了。
image

此时再按一次back键回到MainActivity,再按一次回到桌面
image

使用 Intent标志(Intent.setFlags())

启动 Activity 时,您可以通过在传递给 startActivity() 的 Intent 中加入相应的标志,修改 Activity 与其任务的默认关联方式。(大部分情况下,我们不需要为Activity指定标记位。)

如果 Activity A 启动 Activity B,则 Activity B 可以在其清单文件中定义它应该如何与当前任务关联(如果可能),并且 Activity A 还可以请求 Activity B 应该如何与当前任务关联。如果这两个 Activity 均定义 Activity B 应该如何与任务关联,则 Activity A 的请求(如 Intent 中所定义)优先级要高于 Activity B 的请求(如其清单文件中所定义)。

可用于修改默认行为的标志包括:
1. FLAG_ACTIVITY_NEW_TASK

在新任务中启动Activity。如果已为正在启动的 Activity运行任务,则该任务会转到前台并恢复其最后状态,同时Activity会在onNewIntent()中收到新Intent。正如前文所述,这会产生与”singleTask”launchMode 值相同的行为(TaskAffinity不设置的话,默认情况下的任务栈为应用的包名;非Activity类型的Context,如Application,或者从service中启动activity,没有所谓的任务栈,则intent要添加FLAG_ACTIVITY_NEW_TASK标记)。

  1. FLAG_ACTIVITY_SINGLE_TOP

如果正在启动的Activity是当前Activity(位于返回栈的顶部),则现有实例会接收对onNewIntent()的调用,而不是创建Activity的新实例。正如前文所述,这会产生与”singleTop”launchMode 值相同的行为。

  1. FLAG_ACTIVITY_CLEAR_TOP

如果正在启动的Activity已在当前任务中运行,则会销毁当前任务顶部的所有Activity,并通过 onNewIntent()将此Intent传递给Activity 已恢复的实例(现在位于顶部),而不是启动该 Activity的新实例。产生这种行为的launchMode 属性没有值。

FLAG_ACTIVITY_CLEAR_TOP 通常与 FLAG_ACTIVITY_NEW_TASK 结合使用。一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent 的位置。

注:如果指定 Activity 的启动模式为 “standard”,则该 Activity 也会从堆栈中移除,并在其位置启动一个新实例,以便处理传入的 Intent。 这是因为当启动模式为 “standard” 时,将始终为新 Intent 创建新实例。

Intentfilter的匹配规则

启动Activity分为两种,显示调用和隐式调用。原则上一个Intent不应该既是显式调用又是隐式调用,二者共存的话以显式调用为准。(即显式 Intent始终会传递给其目标,无论组件声明的 Intent 过滤器如何均是如此。)

要公布应用可以接收哪些隐式 Intent,请在清单文件中使用 元素为每个应用组件声明一个或多个 Intent 过滤器。每个 Intent 过滤器均根据 Intent 的操作、数据和类别指定自身接受的 Intent 类型。 仅当隐式 Intent 可以通过 Intent 过滤器之一传递时,系统才会将该 Intent 传递给应用组件。

应用组件应当为自身可执行的每个独特作业声明单独的过滤器。例如,图像库应用中的一个 Activity 可能会有两个过滤器,分别用于查看图像和编辑图像。 当 Activity 启动时,它将检查 Intent 并根据 Intent 中的信息决定具体的行为(例如,是否显示编辑器控件)。

一个过滤列表中的action,category和data可以有多个。 所有的action,category、data分别构成不同的类别,同一类别的信息共同约束当前类别的匹配过程。只有一个Intent同时匹配action类别,category类别和data类别才算完全匹配,只有完全匹配才能启动目标Activity。

在 内部,您可以使用以下三个元素中的一个或多个指定要接受的 Intent 类型:

1.action>

在 name 属性中,声明接受的 Intent 操作。该值必须是操作的文本字符串值,而不是类常量。对于定义的action,最好使用你的应用程序的软件包名作为前缀,以确保其唯一性。

action的匹配要求Intent中的action存在且必须和过滤规则中的其中一个action相同,区分大小写。如果没有在一个,过滤器不接受任何Intent

2.category>

在 name 属性中,声明接受的 Intent 类别。该值必须是操作的文本字符串值,而不是类常量。

Intent一旦有category,不管几个,每个都要能够和过滤规则中的任何一个category相同。如果没有category,就是默认的category,即android.intent.category.DEFAULT。为了Activity能够接受隐式调用,配置多个category的时候需要加上默认的category

3.data>

使用一个或多个指定数据 URI 各个方面(scheme、host、port、path 等)和 MIME 类型的属性,声明接受的数据类型。

Intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data。

data由两部分构成,mimeType和URI。mimeType纸媒体类型,比如image/jpge等。URI的结构:://:/[]|[]|[pathPattern],例如content://com.example.project:200/folder/subfolder/etc,scheme、host、port分别表示URI的模式、主机名和端口号,如果scheme或者host未指定那么URI是无效的。为了匹配多个子域,使用星号()到host匹配零个或多个字符,星号必须是主机属性的第一个字符,星号必须是主机属性的第一个字符。port仅当URI指定了scheme和host才有意义。path、pathPattern、pathPrefix都是表示表述路径信息,其中path表示完整的路径信息,pathPrefix表示路径的前缀信息;pathPattern表示完整的路径,但是它里面可以包含通配符

过滤规则没有指定URI的话,URI的schema有默认值为content和file,如果过滤规则中指定了scheme的话,那么就不是默认的scheme了。

如果要为Intent指定完整的data,必须要调用setDataAndType方法。不能先调用setData然后调用setType,因为这两个方法彼此会清除对方的值。

下面两种写法相同

<intent-filter . . . >    <data android:scheme="something" android:host="project.example.com" />    . . .</intent-filter>
<intent-filter . . . >    <data android:scheme="something" />    <data android:host="project.example.com" />    . . .</intent-filter>

判断是否有Activity能够匹配我们的隐式Intent:
1. PackageManager的resolveActivity方法或者Intent的resolveActivity方法:如果找不到就会返回null
2. PackageManager的queryIntentActivities方法,返回所有成功匹配的Activity信息。针对Service和BroadcastReceiver等组件,PackageManager同样提供了类似的方法去获取成功匹配的组件信息

参考:

Develop
API Guides
应用组件
Activity
任务和返回栈

Develop
API Guides
应用组件
Intent 和 Intent 过滤器 接收隐式 Intent

安卓开发艺术探索

intent.setFlags方法中的参数值含义

0 0
原创粉丝点击