偶然发现了Android Developers里面关于Activity的介绍都被翻译成了中文的了,那么就顺着官方文档,整理一下,常用到的知识点吧。
Activity 配置信息
我们创建了Activity之后,需要在manifest里面配置。
声明一个实现应用的部分可视化用户界面的 Activity(一个 Activity子类)。 所有 Activity 都必须由清单文件中的 <activity>
元素表示。 任何未在该处声明的 Activity 都将对系统不可见,并且也永远不会被运行。
常用属性介绍:
- android:alwaysRetainTaskState
正常情况下,当用户从主屏幕重新选择某个任务时,系统会在特定情况下清除该任务(从根 Activity 之上的堆栈中移除所有 Activity)。 系统通常会在用户一段时间(如 30 分钟)内未访问任务时执行此操作。
系统是否始终保持 Activity 所在任务的状态 —“true”表示保持,“false”表示允许系统在特定情况下将任务重置到其初始状态。 默认值为“false”。该属性只对任务的根 Activity 有意义;对于所有其他 Activity,均忽略该属性。
不过,如果该属性的值是“true”,则无论用户如何到达任务,将始终返回到最后状态的任务。 例如,在网络浏览器这类存在大量用户不愿失去的状态(如多个打开的标签)的应用中,该属性会很有用。
- android:configChanges
列出 Activity 将自行处理的配置更改。
在运行时发生配置更改时,默认情况下会关闭 Activity 然后将其重新启动,但使用该属性声明配置将阻止 Activity 重新启动。 Activity 反而会保持运行状态,并且系统会调用其 onConfigurationChanged() 方法。
任何或所有下列字符串均是该属性的有效值。多个值使用“|”分隔 — 例如,“locale|navigation|orientation”。(常用的属性值)
locale <!--语言区域发生了变化用户为文本选择了新的显示语言-->
keyboard <!--键盘类型发生了变化 — 例如,用户插入了一个外置键盘-->
keyboardHidden <!--键盘无障碍功能发生了变化 — 例如,用户显示了硬件键盘-->
screenLayout <!--屏幕布局发生了变化 — 这可能是由激活了其他显示方式所致-->
orientation <!--屏幕方向发生了变化 — 用户旋转了设备-->
screenSize <!--当前可用屏幕尺寸发生了变化-->
layoutDirection <!--布局方向发生了变化。例如,从从左至右 (LTR) 更改为从右至左 (RTL)。 此项为 API 级别 17 中新增配置。-->
- android:exported
Activity 是否可由其他应用的组件启动 —“true”表示可以,“false”表示不可以。若为“false”,则 Activity 只能由同一应用的组件或使用同一用户 ID 的不同应用启动。
默认值取决于 Activity 是否包含 Intent 过滤器。没有任何过滤器意味着 Activity 只能通过指定其确切的类名称进行调用。 这意味着 Activity 专供应用内部使用(因为其他应用不知晓其类名称)。 因此,在这种情况下,默认值为“false”。另一方面,至少存在一个过滤器意味着 Activity 专供外部使用,因此默认值为“true”。
该属性并非限制 Activity 对其他应用开放度的唯一手段。 您还可以利用权限来限制哪些外部实体可以调用 Activity(请参阅 permission 属性)。
- android:launchMode
有关应如何启动 Activity 的指令。共有四种模式与 Intent 对象中的 Activity 标志(FLAG_ACTIVITY_* 常量)协同工作,以确定在调用 Activity 处理 Intent 时应执行的操作。 这些模式是:
“standard”
“singleTop”
“singleTask”
“singleInstance”
默认模式是“standard”。
- android:process
应在其中运行 Activity 的进程的名称。正常情况下,应用的所有组件都在为应用创建的默认进程名称内运行,您无需使用该属性。 但在必要时,您可以使用该属性替换默认进程名称,以便让应用组件散布到多个进程中。
如果为该属性分配的名称以冒号(“:”)开头,则会在需要时创建应用专用的新进程,并且 Activity 会在该进程中运行。如果进程名称以小写字符开头,Activity 将在该名称的全局进程中运行,前提是它拥有相应的权限。这可以让不同应用中的组件共享一个进程,从而减少资源占用。
元素的 process 属性可为所有组件设置一个不同的默认进程名称。
注意:如果有多个进程,要注意Application的多次创建,以及跨进程间的通信等问题。
- android:screenOrientation
Activity 在设备上的显示方向。如果 Activity 是在多窗口模式下运行,系统会忽略该属性。
其值可以是下列任一字符串(常用的属性值):
landscape <!--横向方向(显示的宽度大于高度)-->
portrait <!--纵向方向(显示的高度大于宽度)-->
sensor <!--方向由设备方向传感器决定。显示方向取决于用户如何手持设备,它会在用户旋转设备时发生变化。 但一些设备默认情况下不会旋转到所有四种可能的方向。要允许全部四种方向,请使用 "fullSensor"-->
fullSensor <!--方向由 4 种方向中任一方向的设备方向传感器决定。这与 "sensor" 类似,不同的是它允许所有 4 种可能的屏幕方向,无论设备正常情况下采用什么方向(例如,一些设备正常情况下不使用反向纵向或反向横向,但它支持这些方向)。 API 级别 9 中的新增配置-->
locked <!--将方向锁定在其当前的任意旋转方向。API 级别 18 中的新增配置-->
- android:windowSoftInputMode
Activity 的主窗口与包含屏幕软键盘的窗口的交互方式。 该属性的设置影响两个方面:
1.当 Activity 成为用户注意的焦点时软键盘的状态 — 隐藏还是可见。
2.对 Activity 主窗口所做的调整 — 是否将其尺寸调小以为软键盘腾出空间,或者当窗口部分被软键盘遮挡时是否平移其内容以使当前焦点可见。
该设置必须是下表所列的值之一,或者是一个“state…”值加上一个“adjust…”值的组合。 在任一组中设置多个值(例如,多个“state…”值)都会产生未定义结果。各值之间使用垂直条 ( | ) 分隔。 例如:
<activity android:windowSoftInputMode="stateVisible|adjustResize" . . . >
此处设置的值(“stateUnspecified”和“adjustUnspecified”除外)替换主题中设置的值
值 | 说明 |
---|---|
“stateUnspecified” | 不指定软键盘的状态(隐藏还是可见)。 将由系统选择合适的状态,或依赖主题中的设置。这是对软键盘行为的默认设置。 |
“stateUnchanged” | 当 Activity 转至前台时保留软键盘最后所处的任何状态,无论是可见还是隐藏 。 |
“stateHidden” | 当用户选择 Activity 时 — 也就是说,当用户确实是向前导航到 Activity,而不是因离开另一 Activity 而返回时 — 隐藏软键盘。 |
“stateAlwaysHidden” | 当 Activity 的主窗口有输入焦点时始终隐藏软键盘。 |
“stateVisible” | 在正常的适宜情况下(当用户向前导航到 Activity 的主窗口时)显示软键盘。 |
“stateAlwaysVisible” | 当用户选择 Activity 时 — 也就是说,当用户确实是向前导航到 Activity,而不是因离开另一 Activity 而返回时 — 显示软键盘。 |
“adjustUnspecified” | 不指定 Activity 的主窗口是否调整尺寸以为软键盘腾出空间,或者窗口内容是否进行平移以在屏幕上显露当前焦点。 系统会根据窗口的内容是否存在任何可滚动其内容的布局视图来自动选择其中一种模式。 如果存在这样的视图,窗口将进行尺寸调整,前提是可通过滚动在较小区域内看到窗口的所有内容。这是对主窗口行为的默认设置。 |
“adjustResize” | 始终调整 Activity 主窗口的尺寸来为屏幕上的软键盘腾出空间。 |
“adjustPan” | 不调整 Activity 主窗口的尺寸来为软键盘腾出空间, 而是自动平移窗口的内容,使当前焦点永远不被键盘遮盖,让用户始终都能看到其输入的内容。 这通常不如尺寸调正可取,因为用户可能需要关闭软键盘以到达被遮盖的窗口部分或与这些部分进行交互。 |
自行处理配置变更
如果应用在特定配置变更期间无需更新资源,并且因性能限制您需要尽量避免重启,则可声明 Activity 将自行处理配置变更,这样可以阻止系统重启 Activity。
从 Android 3.2(API 级别 13)开始,当设备在纵向和横向之间切换时,“屏幕尺寸”也会发生变化。因此,在开发针对 API 级别 13 或更高版本(正如 minSdkVersion 和 targetSdkVersion 属性中所声明)的应用时,若要避免由于设备方向改变而导致运行时重启,则除了 “orientation” 值以外,您还必须添加 “screenSize” 值。 也就是说,您必须声明 android:configChanges="orientation|screenSize"
。
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize"
android:label="@string/app_name">
现在,当其中一个配置发生变化时,MyActivity 不会重启。相反,MyActivity 会收到对 onConfigurationChanged()
的调用。向此方法传递 Configuration 对象指定新设备配置。您可以通过读取 Configuration 中的字段,确定新配置,然后通过更新界面中使用的资源进行适当的更改。调用此方法时,Activity 的 Resources 对象会相应地进行更新,以根据新配置返回资源,这样,您就能够在系统不重启 Activity 的情况下轻松重置 UI 的元素。
例如,以下 onConfigurationChanged()
实现检查当前设备方向:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
任务和返回栈
- 任务模式
“standard”(默认模式)
默认。系统在启动 Activity 的任务中创建 Activity 的新实例并向其传送 Intent。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。
“singleTop”
如果当前任务的顶部已存在 Activity 的一个实例,则系统会通过调用该实例的 onNewIntent()
方法向其传送 Intent,而不是创建 Activity 的新实例。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例(但前提是位于返回栈顶部的 Activity 并不是 Activity 的现有实例)。
例如,假设任务的返回栈包含根 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 的状态。
“singleTask”
系统创建新任务并实例化位于新任务底部的 Activity。但是,如果该 Activity 的一个实例已存在于一个单独的任务中,则系统会通过调用现有实例的 onNewIntent() 方法向其传送 Intent,而不是创建新实例。一次只能存在 Activity 的一个实例。
注:尽管 Activity 在新任务中启动,但是用户按“返回”按钮仍会返回到前一个 Activity。
“singleInstance”
与 “singleTask” 相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中。该 Activity 始终是其任务唯一仅有的成员;由此 Activity 启动的任何 Activity 均在单独的任务中打开。
处理关联
“关联”指示 Activity 优先属于哪个任务。默认情况下,同一应用中的所有 Activity 彼此关联。 因此,默认情况下,同一应用中的所有 Activity 优先位于相同任务中。 不过,您可以修改 Activity 的默认关联。 在不同应用中定义的 Activity 可以共享关联,或者可为在同一应用中定义的 Activity 分配不同的任务关联。
可以使用 <activity>
元素的 taskAffinity
属性修改任何给定 Activity 的关联。
taskAffinity 属性取字符串值,该值必须不同于在 <manifest>
元素中声明的默认软件包名称,因为系统使用该名称标识应用的默认任务关联。
在两种情况下,关联会起作用:
1.启动 Activity 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK
标志。
默认情况下,新 Activity 会启动到调用 startActivity()
的 Activity 任务中。它将推入与调用方相同的返回栈。 但是,如果传递给 startActivity() 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK
标志,则系统会寻找其他任务来储存新 Activity。这通常是新任务,但未做强制要求。 如果现有任务与新 Activity 具有相同关联,则会将 Activity 启动到该任务中。 否则,将开始新任务。
如果此标志导致 Activity 开始新任务,且用户按“主页”按钮离开,则必须为用户提供导航回任务的方式。 有些实体(如通知管理器)始终在外部任务中启动 Activity,而从不作为其自身的一部分启动 Activity,因此它们始终将 FLAG_ACTIVITY_NEW_TASK
放入传递给 startActivity()
的 Intent 中。
例如:用户启动一个Activity。效果图如下:
<activity
android:name=".ActivityC"
android:label="@string/title_activity_c"
android:taskAffinity="com.activity_c"
android:launchMode="singleTask"
android:theme="@style/AppTheme.NoActionBar" />
2.Activity 将其 allowTaskReparenting
属性设置为 “true”。
在这种情况下,Activity 可以从其启动的任务移动到与其具有关联的任务(如果该任务出现在前台)。
例如,假设将报告所选城市天气状况的 Activity 定义为旅行应用的一部分。 它与同一应用中的其他 Activity 具有相同的关联(默认应用关联),并允许利用此属性重定父级。当您的一个 Activity 启动天气预报 Activity 时,它最初所属的任务与您的 Activity 相同。 但是,当旅行应用的任务出现在前台时,系统会将天气预报 Activity 重新分配给该任务并显示在其中。
提示:如果从用户的角度来看,一个 .apk 文件包含多个“应用”,则您可能需要使用 taskAffinity 属性将不同关联分配给与每个“应用”相关的 Activity。
例如:在一个应用中,启动另一个应用的Activity。效果图中,TestNetWork应用启动TestActivity应用的LoginActivity。其中
LoginActivity设置了 allowTaskReparenting
属性设置为 “true”。启动模式都是默认的启动模式。
能从效果图里面看出什么逻辑,请自行脑补。(`・ω・´)
启动模式对比
四种模式分为两大类,“standard”和“singleTop”Activity 为一类,“singleTask”和“singleInstance”为另一类。使用“standard”或“singleTop”启动模式的 Activity 可多次实例化。 实例可归属任何任务,并且可以位于 Activity 堆栈中的任何位置。 它们通常启动到名为 startActivity() 的任务之中(除非 Intent 对象包含 FLAG_ACTIVITY_NEW_TASK 指令,在此情况下会选择其他任务 — 请参阅 taskAffinity 属性)。
相比之下,“singleTask”和“singleInstance”Activity 只能启动任务。 它们始终位于 Activity 堆栈的根位置。此外,设备一次只能保留一个 Activity 实例 — 只允许一个此类任务。
1.“standard”和“singleTop”模式只在一个方面有差异: 每次“standard”Activity 有新的 Intent 时,系统都会创建新的类实例来响应该 Intent。每个实例处理单个 Intent。同理,也可创建新的“singleTop”Activity 实例来处理新的 Intent。 不过,如果目标任务在其堆栈顶部已有一个 Activity 实例,那么该实例将接收新 Intent(通过调用 onNewIntent());此时不会创建新实例。在其他情况下 — 例如,如果“singleTop”的一个现有实例虽在目标任务内,但未处于堆栈顶部,或者虽然位于堆栈顶部,但不在目标任务中 — 则系统会创建一个新实例并将其推送到堆栈上。
同理,如果您向上导航到当前堆栈上的某个 Activity,该行为由父 Activity 的启动模式决定。 如果父 Activity 有启动模式 singleTop(或 up Intent 包含 FLAG_ACTIVITY_CLEAR_TOP),则系统会将该父项置于堆栈顶部,并保留其状态。
导航 Intent 由父 Activity 的 onNewIntent() 方法接收。 如果父 Activity 有启动模式 standard(并且 up Intent 不包含 FLAG_ACTIVITY_CLEAR_TOP),则系统会将当前 Activity 及其父项同时弹出堆栈,并创建一个新的父 Activity 实例来接收导航 Intent。
2.“singleTask”和“singleInstance”模式同样只在一个方面有差异: “singleTask”Activity 允许其他 Activity 成为其任务的组成部分。 它始终位于其任务的根位置,但其他 Activity(必然是“standard”和“singleTop”Activity)可以启动到该任务中。 相反,“singleInstance”Activity 则不允许其他 Activity 成为其任务的组成部分。它是任务中唯一的 Activity。 如果它启动另一个 Activity,系统会将该 Activity 分配给其他任务 — 就好像 Intent 中包含 FLAG_ACTIVITY_NEW_TASK 一样。
3.standard 是默认模式,并且适用于大多数的 Activity 类型。对许多类型的 Activity 而言,SingleTop 也是一个常见并且有用的启动模式。 其他模式 — singleTask 和 singleInstance - 不适合 大多数应用因为它们所形成的交互模式可能让用户感到陌生,并且与大多数其他应用迥异。
Activity生命周期
生命周期图,为了使用方便直接把Fragment也贴上来了:
Activity生命周期介绍:
方法 | 说明 | 是否能事后终止? | 后接 | ||
---|---|---|---|---|---|
| 首次创建 Activity 时调用。 您应该在此方法中执行所有正常的静态设置 — 创建视图、将数据绑定到列表等等。 系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态(请参阅后文的保存 Activity 状态)。 始终后接 | 否 | onStart() | ||
| 在 Activity 已停止并即将再次启动前调用。 始终后接 | 否 | onStart() | ||
| 在 Activity 即将对用户可见之前调用。 如果 Activity 转入前台,则后接 | 否 | onResume() 或 onStop() | ||
| 在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。 始终后接 | 否 | onPause() | ||
| 当系统即将开始继续另一个 Activity 时调用。 此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。 如果 Activity 返回前台,则后接 | 是 | onResume() 或 onStop() | ||
| 在 Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)继续执行并将其覆盖,就可能发生这种情况。 如果 Activity 恢复与用户的交互,则后接 | 是 | onRestart() 或 onDestroy() | ||
| 在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 当 Activity 结束(有人对 Activity 调用了 ),或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 您可以通过 方法区分这两种情形。 | 是 | 无 |
名为“是否能事后终止?”的列表示系统是否能在不执行另一行 Activity 代码的情况下,在方法返回后随时终止承载 Activity 的进程。 有三个方法带有“是”标记:(onPause()
、onStop()
和 onDestroy()
)。由于 onPause()
是这三个方法中的第一个,因此 Activity 创建后,onPause()
必定成为最后调用的方法,然后才能终止进程 — 如果系统在紧急情况下必须恢复内存,则可能不会调用 onStop()
和 onDestroy()
。因此,您应该使用 onPause()
向存储设备写入至关重要的持久性数据(例如用户编辑)。不过,您应该对 onPause()
调用期间必须保留的信息有所选择,因为该方法中的任何阻止过程都会妨碍向下一个 Activity 的转变并拖慢用户体验。
在是否能在事后终止?列中标记为“否”的方法可从系统调用它们的一刻起防止承载 Activity 的进程被终止。 因此,在从 onPause()
返回的时间到 onResume()
被调用的时间,系统可以终止 Activity。在 onPause()
被再次调用并返回前,将无法再次终止 Activity。