当用户在您的应用中导航、退出并返回时,应用中的 Activity
实例在其生命周期中会经历不同的状态。 Activity
类提供了一些回调,让 Activity 知道何时状态发生变化,或者系统正在创建、停止或恢复 Activity,或者销毁 Activity 所在的进程。
在生命周期回调方法中,您可以声明当用户离开并重新进入 Activity 时 Activity 的行为。例如,如果您正在构建一个流媒体视频播放器,您可能会在用户切换到另一个应用时暂停视频并终止网络连接。当用户返回时,您可以重新连接到网络并让用户从相同的位置恢复视频。
每个回调都允许您执行适合特定状态变化的特定工作。在正确的时间执行正确的工作并正确处理转换可以使您的应用更健壮和更高效。例如,生命周期回调的良好实现可以帮助您的应用避免以下情况
- 如果用户在使用您的应用时接听电话或切换到另一个应用,则应用崩溃。
- 当用户没有积极使用应用时,消耗宝贵的系统资源。
- 如果用户离开您的应用并在稍后返回,则会丢失用户的进度。
- 当屏幕在横向和纵向之间旋转时,应用崩溃或丢失用户的进度。
本文档详细解释了 Activity 生命周期。本文档首先描述生命周期范例。接下来,它解释了每个回调:在执行期间内部发生的事情以及您需要在其中实现的内容。
然后简要介绍了 Activity 状态与进程被系统终止的脆弱性之间的关系。最后,它讨论了与 Activity 状态之间转换相关的几个主题。
有关处理生命周期的信息,包括最佳实践指南,请参阅 使用生命周期感知组件处理生命周期 和 保存 UI 状态。要了解如何使用 Activity 结合架构组件设计一个健壮的、生产级别的应用,请参阅 应用架构指南。
Activity 生命周期概念
为了导航 Activity 生命周期各个阶段之间的转换,Activity
类提供了一组核心六个回调:onCreate()
、onStart()
、onResume()
、onPause()
、onStop()
和 onDestroy()
。系统在 Activity 进入新状态时调用每个回调。
图 1 显示了此范例的可视化表示。
当用户开始离开 Activity 时,系统会调用方法来拆卸 Activity。在某些情况下,Activity 仅被部分拆卸并仍驻留在内存中,例如当用户切换到另一个应用时。在这些情况下,Activity 仍然可以返回到前台。
如果用户返回到 Activity,它将从用户离开的地方恢复。除了少数例外情况,应用 限制在后台运行时启动 Activity。
系统终止给定进程(以及其中的 Activity)的可能性取决于 Activity 当时的狀態。有关状态与被逐出内存的脆弱性之间关系的更多信息,请参阅有关 Activity 状态和从内存中逐出 的部分。
根据 Activity 的复杂性,您可能不需要实现所有生命周期方法。但是,了解每个方法并实现使您的应用按用户预期方式运行的方法非常重要。
生命周期回调
本节提供了有关 Activity 生命周期期间使用的回调方法的概念和实现信息。
某些操作属于 Activity 生命周期方法。但是,将实现依赖组件操作的代码放在组件中,而不是 Activity 生命周期方法中。为此,您需要使依赖组件成为生命周期感知的。要了解如何使依赖组件成为生命周期感知的,请参阅 使用生命周期感知组件处理生命周期。
onCreate()
您必须实现此回调,该回调在系统首次创建 Activity 时触发。在 Activity 创建时,Activity 进入 已创建 状态。在 onCreate()
方法中,执行仅对 Activity 的整个生命周期执行一次的基本应用启动逻辑。
例如,您对onCreate()
的实现可能会将数据绑定到列表、将活动与ViewModel
关联,并实例化一些类范围变量。此方法接收参数savedInstanceState
,它是一个Bundle
对象,包含活动先前保存的状态。如果活动以前从未存在过,则Bundle
对象的值为null。
如果您有一个与活动生命周期挂钩的生命周期感知组件,它将接收ON_CREATE
事件。使用@OnLifecycleEvent
注释的方法被调用,以便您的生命周期感知组件可以执行其为已创建状态所需的任何设置代码。
以下onCreate()
方法示例显示了活动的基本设置,例如声明用户界面(在 XML 布局文件中定义)、定义成员变量以及配置一些 UI。在此示例中,XML 布局文件将文件的资源 ID R.layout.main_activity
传递给setContentView()
。
Kotlin
lateinit var textView: TextView // Some transient state for the activity instance. var gameState: String? = null override fun onCreate(savedInstanceState: Bundle?) { // Call the superclass onCreate to complete the creation of // the activity, like the view hierarchy. super.onCreate(savedInstanceState) // Recover the instance state. gameState = savedInstanceState?.getString(GAME_STATE_KEY) // Set the user interface layout for this activity. // The layout is defined in the project res/layout/main_activity.xml file. setContentView(R.layout.main_activity) // Initialize member TextView so it is available later. textView = findViewById(R.id.text_view) } // This callback is called only when there is a saved instance previously saved using // onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally // be restored here, possibly usable after onStart() has completed. // The savedInstanceState Bundle is same as the one used in onCreate(). override fun onRestoreInstanceState(savedInstanceState: Bundle?) { textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY) } // Invoked when the activity might be temporarily destroyed; save the instance state here. override fun onSaveInstanceState(outState: Bundle?) { outState?.run { putString(GAME_STATE_KEY, gameState) putString(TEXT_VIEW_KEY, textView.text.toString()) } // Call superclass to save any view hierarchy. super.onSaveInstanceState(outState) }
Java
TextView textView; // Some transient state for the activity instance. String gameState; @Override public void onCreate(Bundle savedInstanceState) { // Call the superclass onCreate to complete the creation of // the activity, like the view hierarchy. super.onCreate(savedInstanceState); // Recover the instance state. if (savedInstanceState != null) { gameState = savedInstanceState.getString(GAME_STATE_KEY); } // Set the user interface layout for this activity. // The layout is defined in the project res/layout/main_activity.xml file. setContentView(R.layout.main_activity); // Initialize member TextView so it is available later. textView = (TextView) findViewById(R.id.text_view); } // This callback is called only when there is a saved instance previously saved using // onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally // be restored here, possibly usable after onStart() has completed. // The savedInstanceState Bundle is same as the one used in onCreate(). @Override public void onRestoreInstanceState(Bundle savedInstanceState) { textView.setText(savedInstanceState.getString(TEXT_VIEW_KEY)); } // Invoked when the activity might be temporarily destroyed; save the instance state here. @Override public void onSaveInstanceState(Bundle outState) { outState.putString(GAME_STATE_KEY, gameState); outState.putString(TEXT_VIEW_KEY, textView.getText()); // Call superclass to save any view hierarchy. super.onSaveInstanceState(outState); }
作为定义 XML 文件并将其传递给setContentView()
的替代方法,您可以在活动代码中创建新的View
对象,并通过将新的View
对象插入到ViewGroup
中构建视图层次结构。然后,通过将根ViewGroup
传递给setContentView()
来使用该布局。有关创建用户界面的更多信息,请参阅用户界面文档。
您的活动不会一直处于 Created 状态。在onCreate()
方法执行完成之后,活动将进入Started状态,系统将依次快速调用onStart()
和onResume()
方法。
onStart()
当活动进入 Started 状态时,系统会调用onStart()
。此调用使活动对用户可见,因为应用程序正在准备让活动进入前台并变得可交互。例如,此方法是初始化维护 UI 的代码的位置。
当活动进入 Started 状态时,任何与活动生命周期绑定的生命周期感知组件都会收到ON_START
事件。
onStart()
方法完成得很快,并且与 Created 状态一样,活动不会一直处于 Started 状态。此回调完成后,活动将进入Resumed状态,系统会调用onResume()
方法。
onResume()
当活动进入 Resumed 状态时,它将进入前台,系统会调用onResume()
回调。这是应用程序与用户交互的状态。应用程序将一直保持此状态,直到发生某些事件将焦点从应用程序中移开,例如设备收到电话、用户导航到另一个活动或设备屏幕关闭。
当活动进入 Resumed 状态时,任何与活动生命周期绑定的生命周期感知组件都会收到ON_RESUME
事件。这是生命周期组件可以启用任何需要在组件可见且位于前台时运行的功能(例如启动相机预览)的地方。
当发生中断事件时,活动将进入Paused状态,系统会调用onPause()
回调。
如果活动从 Paused 状态返回到 Resumed 状态,系统将再次调用onResume()
方法。因此,实现onResume()
以初始化在onPause()
期间释放的组件,并执行每次活动进入 Resumed 状态时必须执行的其他初始化。
这是一个生命周期感知组件的示例,它在组件接收到ON_RESUME
事件时访问相机
Kotlin
class CameraComponent : LifecycleObserver { ... @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun initializeCamera() { if (camera == null) { getCamera() } } ... }
Java
public class CameraComponent implements LifecycleObserver { ... @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) public void initializeCamera() { if (camera == null) { getCamera(); } } ... }
前面的代码在LifecycleObserver
收到ON_RESUME
事件后初始化相机。但是,在多窗口模式下,即使活动处于 Paused 状态,它也可能完全可见。例如,当应用程序处于多窗口模式并且用户点击不包含您活动的窗口时,您的活动将进入 Paused 状态。
如果您希望相机仅在应用程序处于 Resumed 状态(在前台可见且处于活动状态)时处于活动状态,则在前面演示的ON_RESUME
事件之后初始化相机。如果您希望在活动处于 Paused 状态但可见时(例如在多窗口模式下)保持相机处于活动状态,则在ON_START
事件之后初始化相机。
但是,在您的活动处于 Paused 状态时使相机保持活动状态可能会拒绝另一个在多窗口模式下处于 Resumed 状态的应用程序访问相机。有时需要在活动处于 Paused 状态时保持相机处于活动状态,但如果您这样做,实际上可能会降低整体用户体验。
因此,请仔细考虑在生命周期的哪个位置以多窗口模式控制共享系统资源最合适。要了解有关支持多窗口模式的更多信息,请参阅多窗口支持。
无论您选择在哪个构建事件中执行初始化操作,请确保使用相应生命周期事件来释放资源。如果您在ON_START
事件之后初始化某些内容,请在ON_STOP
事件之后释放或终止它。如果您在ON_RESUME
事件之后初始化,请在ON_PAUSE
事件之后释放。
前面的代码片段将相机初始化代码放在生命周期感知组件中。您也可以将此代码直接放入活动生命周期回调中,例如onStart()
和onStop()
,但我们不建议这样做。将此逻辑添加到独立的生命周期感知组件中,可以让您在多个活动中重用该组件,而无需重复代码。要了解如何创建生命周期感知组件,请参阅使用生命周期感知组件处理生命周期。
onPause()
系统调用此方法作为用户离开您的活动的第一个指示,尽管这并不总是意味着活动将被销毁。它表示活动不再位于前台,但如果用户处于多窗口模式,它仍然可见。活动可能进入此状态的原因有很多
- 中断应用程序执行的事件(如有关onResume()回调的部分中所述)会暂停当前活动。这是最常见的情况。
- 在多窗口模式下,任何时候只有一个应用程序具有焦点,系统会暂停所有其他应用程序。
- 打开新的半透明活动(例如对话框)会暂停它覆盖的活动。只要活动部分可见但未处于焦点状态,它就会保持暂停状态。
当活动进入 Paused 状态时,任何与活动生命周期绑定的生命周期感知组件都会收到ON_PAUSE
事件。这是生命周期组件可以停止任何在组件不在前台时不需要运行的功能(例如停止相机预览)的地方。
使用onPause()
方法暂停或调整在Activity
处于 Paused 状态时无法继续或可能适度继续的操作,并且您希望在不久的将来恢复这些操作。
您还可以使用onPause()
方法释放系统资源、传感器句柄(如 GPS)或在活动处于 Paused 状态且用户不需要时会影响电池寿命的任何资源。
但是,如有关onResume()
的部分所述,如果应用程序处于多窗口模式,则 Paused 活动可能仍然完全可见。考虑使用onStop()
而不是onPause()
来完全释放或调整与 UI 相关的资源和操作,以更好地支持多窗口模式。
以下LifecycleObserver
对ON_PAUSE
事件做出反应的示例是前面ON_RESUME
事件示例的对应部分,它释放了在接收到ON_RESUME
事件后初始化的相机
Kotlin
class CameraComponent : LifecycleObserver { ... @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun releaseCamera() { camera?.release() camera = null } ... }
Java
public class JavaCameraComponent implements LifecycleObserver { ... @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void releaseCamera() { if (camera != null) { camera.release(); camera = null; } } ... }
此示例将相机释放代码放在LifecycleObserver
接收到ON_PAUSE
事件之后。
onPause()
的执行非常短暂,不一定有足够的时间执行保存操作。因此,**不要**使用onPause()
保存应用程序或用户数据、进行网络调用或执行数据库事务。此类工作可能在方法完成之前无法完成。
相反,在onStop()
期间执行繁重的关闭操作。有关在onStop()
期间执行的合适操作的更多信息,请参阅下一节。有关保存数据的更多信息,请参阅有关保存和恢复状态的部分。
onPause()
方法的完成并不意味着活动离开 Paused 状态。相反,活动将保持此状态,直到活动恢复或对用户完全不可见。如果活动恢复,系统将再次调用onResume()
回调。
如果活动从 Paused 状态返回到 Resumed 状态,系统会将Activity
实例驻留在内存中,并在系统调用onResume()
时调用该实例。在这种情况下,您不需要重新初始化在任何导致 Resumed 状态的回调方法期间创建的组件。如果活动变得完全不可见,系统会调用onStop()
。
onStop()
当您的活动不再对用户可见时,它将进入Stopped状态,系统会调用onStop()
回调。当新启动的活动覆盖整个屏幕时,可能会发生这种情况。当活动完成运行并即将终止时,系统也会调用onStop()
。
当活动进入 Stopped 状态时,任何与活动生命周期绑定的生命周期感知组件都会收到ON_STOP
事件。这是生命周期组件可以停止任何在组件不在屏幕上可见时不需要运行的功能的地方。
在onStop()
方法中,释放或调整在应用程序对用户不可见时不需要的资源。例如,您的应用程序可能会暂停动画或从细粒度位置更新切换到粗粒度位置更新。使用onStop()
而不是onPause()
意味着 UI 相关的工作会继续,即使用户在多窗口模式下查看您的活动也是如此。
此外,使用 onStop()
执行相对占用 CPU 资源较多的关闭操作。例如,如果您找不到更好的时间将信息保存到数据库,则可以在 onStop()
中执行此操作。以下示例展示了 onStop()
的实现,该实现将草稿笔记的内容保存到持久性存储中
Kotlin
override fun onStop() { // Call the superclass method first. super.onStop() // Save the note's current draft, because the activity is stopping // and we want to be sure the current note progress isn't lost. val values = ContentValues().apply { put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText()) put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle()) } // Do this update in background on an AsyncQueryHandler or equivalent. asyncQueryHandler.startUpdate( token, // int token to correlate calls null, // cookie, not used here uri, // The URI for the note to update. values, // The map of column names and new values to apply to them. null, // No SELECT criteria are used. null // No WHERE columns are used. ) }
Java
@Override protected void onStop() { // Call the superclass method first. super.onStop(); // Save the note's current draft, because the activity is stopping // and we want to be sure the current note progress isn't lost. ContentValues values = new ContentValues(); values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText()); values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle()); // Do this update in background on an AsyncQueryHandler or equivalent. asyncQueryHandler.startUpdate ( mToken, // int token to correlate calls null, // cookie, not used here uri, // The URI for the note to update. values, // The map of column names and new values to apply to them. null, // No SELECT criteria are used. null // No WHERE columns are used. ); }
前面的代码示例直接使用了 SQLite。但是,我们建议使用 Room,这是一个持久性库,它为 SQLite 提供了抽象层。要了解有关使用 Room 的优势以及如何在应用中实现 Room 的更多信息,请参阅 Room 持久性库 指南。
当您的活动进入停止状态时,Activity
对象会保留在内存中:它会维护所有状态和成员信息,但不会附加到窗口管理器。当活动恢复时,它会回忆起这些信息。
您无需重新初始化在任何导致恢复状态的回调方法期间创建的组件。系统还会跟踪布局中每个 View
对象的当前状态,因此,如果用户在 EditText
组件中输入文本,则该内容将被保留,因此您无需保存和恢复它。
注意:一旦您的活动停止,如果系统需要回收内存,则系统可能会销毁包含该活动的进程。即使系统在活动停止期间销毁了进程,系统仍然会将 View
对象的状态(例如,EditText
组件中的文本)保存在 Bundle
中(一个键值对的 blob),并在用户导航回活动时恢复它们。有关将活动恢复到用户返回到的活动的信息,请参阅有关 保存和恢复状态 的部分。
从停止状态开始,活动要么恢复与用户的交互,要么活动运行结束并消失。如果活动恢复,系统将调用 onRestart()
。如果 Activity
运行结束,系统将调用 onDestroy()
。
onDestroy()
onDestroy()
在活动被销毁之前调用。系统出于以下两个原因之一调用此回调
- 活动即将结束,因为用户完全关闭了活动或因为在活动上调用了
finish()
。 - 系统由于配置更改(例如设备旋转或进入多窗口模式)而暂时销毁活动。
当活动移动到销毁状态时,任何与活动生命周期绑定的生命周期感知组件都会收到 ON_DESTROY
事件。这是生命周期组件在 Activity
被销毁之前可以清理任何需要清理的内容的地方。
不要在您的 Activity
中放置逻辑来确定它为什么被销毁,而是使用 ViewModel
对象来包含 Activity
的相关视图数据。如果 Activity
由于配置更改而重新创建,则 ViewModel
不必执行任何操作,因为它会被保留并提供给下一个 Activity
实例。
如果 Activity
没有重新创建,则 ViewModel
会调用 onCleared()
方法,它可以在被销毁之前清理任何需要清理的数据。您可以使用 isFinishing()
方法区分这两种情况。
如果活动即将结束,则 onDestroy()
是活动接收的最后一个生命周期回调。如果 onDestroy()
是配置更改的结果而被调用的,则系统会立即创建一个新的活动实例,然后在新的配置中对该新实例调用 onCreate()
。
onDestroy()
回调释放了之前回调(例如 onStop()
)未释放的所有资源。
活动状态和内存弹出
系统在需要释放 RAM 时会终止进程。系统终止给定进程的可能性取决于该进程当时的运行状态。进程状态反过来又取决于进程中运行的活动的运行状态。表 1 显示了进程状态、活动状态和系统终止进程的可能性之间的相关性。此表仅适用于进程未运行其他类型的应用程序组件的情况。
被终止的可能性 | 进程状态 | 最终活动状态 |
---|---|---|
最低 | 前台(拥有或即将获得焦点) | 已恢复 |
低 | 可见(无焦点) | 已启动/已暂停 |
较高 | 后台(不可见) | 已停止 |
最高 | 空 | 已销毁 |
表 1. 进程生命周期和活动状态之间的关系。
系统永远不会直接终止活动来释放内存。相反,它会终止活动所在的进程,从而不仅销毁活动,还会销毁进程中运行的所有其他内容。要了解如何在系统启动的进程终止发生时保留和恢复活动的 UI 状态,请参阅有关 保存和恢复状态 的部分。
用户还可以通过在“设置”下的“应用程序管理器”中使用“终止”相应的应用程序来终止进程。
有关进程的更多信息,请参阅 进程和线程概述。
保存和恢复瞬态 UI 状态
用户期望活动的 UI 状态在整个配置更改(例如旋转或切换到多窗口模式)过程中保持不变。但是,系统在发生此类配置更改时默认会销毁活动,从而清除存储在活动实例中的任何 UI 状态。
类似地,用户期望如果他们暂时从您的应用切换到另一个应用,然后稍后返回您的应用,UI 状态保持不变。但是,系统可以在用户离开并且您的活动已停止时销毁您的应用的进程。
当系统约束销毁活动时,请使用 ViewModel
、 onSaveInstanceState()
和/或本地存储的组合来保留用户的瞬态 UI 状态。要了解有关用户期望与系统行为的比较以及如何在系统启动的活动和进程终止中最佳地保留复杂 UI 状态数据的更多信息,请参阅 保存 UI 状态。
本节概述了实例状态是什么以及如何实现 onSaveInstance()
方法,该方法是活动本身的回调。如果您的 UI 数据很轻量级,则可以单独使用 onSaveInstance()
在配置更改和系统启动的进程终止中持久化 UI 状态。但是,由于 onSaveInstance()
会产生序列化/反序列化成本,因此在大多数情况下,您会同时使用 ViewModel
和 onSaveInstance()
,如 保存 UI 状态 中所述。
注意:要了解有关配置更改、如何在需要时限制活动重新创建以及如何从 View 系统和 Jetpack Compose 对这些配置更改做出反应的更多信息,请查看 处理配置更改 页面。
实例状态
在一些情况下,您的活动会因正常的应用行为而被销毁,例如,当用户按下“后退”按钮或您的活动通过调用 finish()
方法来发出其自身销毁的信号时。
当您的活动因用户按下“后退”或活动自身结束而被销毁时,系统和用户对该 Activity
实例的概念都将永远消失。在这些情况下,用户的期望与系统的行为相符,您无需执行任何额外工作。
但是,如果系统由于系统约束(例如配置更改或内存压力)而销毁活动,那么尽管实际的 Activity
实例消失了,但系统会记住它曾经存在。如果用户尝试导航回活动,系统将使用一组保存的数据创建该活动的新实例,这些数据描述了活动在被销毁时的状态。
系统用于恢复先前状态的保存数据称为实例状态。它是在 Bundle
对象中存储的一组键值对。默认情况下,系统使用 Bundle
实例状态来保存有关活动布局中每个 View
对象的信息,例如输入到 EditText
组件中的文本值。
因此,如果您的活动实例被销毁并重新创建,则布局的状态将恢复到其先前状态,而无需您编写任何代码。但是,您的活动可能还有更多您希望恢复的状态信息,例如跟踪用户在活动中进度的成员变量。
注意:为了使 Android 系统能够恢复活动中视图的状态,每个视图都必须具有唯一的 ID,该 ID 由 android:id
属性提供。
Bundle
对象不适合保存超过少量数据,因为它需要在主线程上进行序列化并消耗系统进程内存。要保存超过少量数据,请采用一种组合方法来保存数据,使用持久性本地存储、onSaveInstanceState()
方法和 ViewModel
类,如 保存 UI 状态 中所述。
使用 onSaveInstanceState() 保存简单、轻量级的 UI 状态
当您的 Activity 开始停止时,系统会调用 onSaveInstanceState()
方法,以便您的 Activity 可以将状态信息保存到实例状态 Bundle 中。此方法的默认实现保存了 Activity 的视图层次结构状态的瞬态信息,例如 EditText
控件中的文本或 ListView
控件的滚动位置。
要为您的 Activity 保存其他实例状态信息,请覆盖 onSaveInstanceState()
并将键值对添加到 Bundle
对象中,该对象在您的 Activity 意外销毁时保存。当您覆盖 onSaveInstanceState()
时,如果希望默认实现保存视图层次结构的状态,则需要调用超类实现。以下示例显示了这一点
Kotlin
override fun onSaveInstanceState(outState: Bundle?) { // Save the user's current game state. outState?.run { putInt(STATE_SCORE, currentScore) putInt(STATE_LEVEL, currentLevel) } // Always call the superclass so it can save the view hierarchy state. super.onSaveInstanceState(outState) } companion object { val STATE_SCORE = "playerScore" val STATE_LEVEL = "playerLevel" }
Java
static final String STATE_SCORE = "playerScore"; static final String STATE_LEVEL = "playerLevel"; // ... @Override public void onSaveInstanceState(Bundle savedInstanceState) { // Save the user's current game state. savedInstanceState.putInt(STATE_SCORE, currentScore); savedInstanceState.putInt(STATE_LEVEL, currentLevel); // Always call the superclass so it can save the view hierarchy state. super.onSaveInstanceState(savedInstanceState); }
注意: 当用户显式关闭 Activity 或在其他情况下调用 finish()
时,不会调用 onSaveInstanceState()
。
要保存持久性数据(例如用户偏好设置或数据库数据),请在 Activity 处于前台时抓住适当的机会。如果没有这样的机会,请在 onStop()
方法期间保存持久性数据。
使用保存的实例状态恢复 Activity UI 状态
当您的 Activity 在之前被销毁后重新创建时,您可以从系统传递给 Activity 的 Bundle
中恢复保存的实例状态。onCreate()
和 onRestoreInstanceState()
回调方法都接收包含实例状态信息的相同 Bundle
。
由于 onCreate()
方法是在系统创建 Activity 的新实例还是重新创建之前的实例时都会调用,因此在尝试读取状态 Bundle
之前,您需要检查它是否为 null。如果它为 null,则系统正在创建 Activity 的新实例,而不是恢复之前被销毁的实例。
以下代码片段显示了如何在 onCreate()
中恢复一些状态数据
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Always call the superclass first // Check whether we're recreating a previously destroyed instance. if (savedInstanceState != null) { with(savedInstanceState) { // Restore value of members from saved state. currentScore = getInt(STATE_SCORE) currentLevel = getInt(STATE_LEVEL) } } else { // Probably initialize members with default values for a new instance. } // ... }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Always call the superclass first // Check whether we're recreating a previously destroyed instance. if (savedInstanceState != null) { // Restore value of members from saved state. currentScore = savedInstanceState.getInt(STATE_SCORE); currentLevel = savedInstanceState.getInt(STATE_LEVEL); } else { // Probably initialize members with default values for a new instance. } // ... }
您可以选择实现 onRestoreInstanceState()
,而不是在 onCreate()
期间恢复状态,系统在 onStart()
方法之后调用此方法。系统仅在有保存的状态要恢复时才调用 onRestoreInstanceState()
,因此您无需检查 Bundle
是否为 null。
Kotlin
override fun onRestoreInstanceState(savedInstanceState: Bundle?) { // Always call the superclass so it can restore the view hierarchy. super.onRestoreInstanceState(savedInstanceState) // Restore state members from saved instance. savedInstanceState?.run { currentScore = getInt(STATE_SCORE) currentLevel = getInt(STATE_LEVEL) } }
Java
public void onRestoreInstanceState(Bundle savedInstanceState) { // Always call the superclass so it can restore the view hierarchy. super.onRestoreInstanceState(savedInstanceState); // Restore state members from saved instance. currentScore = savedInstanceState.getInt(STATE_SCORE); currentLevel = savedInstanceState.getInt(STATE_LEVEL); }
注意:始终调用 onRestoreInstanceState()
的超类实现,以便默认实现可以恢复视图层次结构的状态。
在 Activity 之间导航
在应用程序的生命周期中,应用程序可能会多次进入和退出 Activity,例如当用户点击设备的返回按钮或 Activity 启动另一个 Activity 时。
本节介绍了成功实现 Activity 过渡所需了解的主题。这些主题包括从另一个 Activity 启动 Activity、保存 Activity 状态和恢复 Activity 状态。
从另一个 Activity 启动一个 Activity
Activity 通常需要在某些时候启动另一个 Activity。例如,当应用程序需要从当前屏幕移动到新屏幕时,就会出现这种需求。
根据您的 Activity 是否希望从即将启动的新 Activity 中获取结果,您可以使用 startActivity()
方法或 startActivityForResult()
方法启动新 Activity。在这两种情况下,您都会传入一个 Intent
对象。
该 Intent
对象指定要启动的确切 Activity 或描述要执行的操作类型。系统会为您选择合适的 Activity,该 Activity 甚至可以来自不同的应用程序。Intent
对象还可以携带少量数据供启动的 Activity 使用。有关 Intent
类的更多信息,请参阅 意图和意图过滤器。
startActivity()
如果新启动的 Activity 不需要返回结果,则当前 Activity 可以通过调用 startActivity()
方法来启动它。
在您自己的应用程序中工作时,您通常只需要启动一个已知的 Activity。例如,以下代码片段显示了如何启动名为 SignInActivity
的 Activity。
Kotlin
val intent = Intent(this, SignInActivity::class.java) startActivity(intent)
Java
Intent intent = new Intent(this, SignInActivity.class); startActivity(intent);
您的应用程序可能还希望使用 Activity 中的数据执行某些操作,例如发送电子邮件、短信或状态更新。在这种情况下,您的应用程序可能没有自己的 Activity 来执行此类操作,因此您可以利用设备上其他应用程序提供的 Activity,这些 Activity 可以为您执行操作。
这就是意图真正有价值的地方。您可以创建一个描述要执行的操作的意图,系统会从另一个应用程序启动相应的 Activity。如果有多个 Activity 可以处理该意图,则用户可以选择使用哪个 Activity。例如,如果您想让用户发送电子邮件,则可以创建以下意图
Kotlin
val intent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_EMAIL, recipientArray) } startActivity(intent)
Java
Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_EMAIL, recipientArray); startActivity(intent);
添加到意图中的 EXTRA_EMAIL
额外信息是电子邮件要发送到的电子邮件地址的字符串数组。当电子邮件应用程序响应此意图时,它会读取额外信息中提供的字符串数组并将地址放置在电子邮件撰写表单的“收件人”字段中。在这种情况下,电子邮件应用程序的 Activity 会启动,当用户完成操作时,您的 Activity 会恢复。
startActivityForResult()
有时,您希望在 Activity 结束时从 Activity 中获取结果。例如,您可能会启动一个 Activity,让用户在联系人列表中选择一个人。当它结束时,它会返回所选的人员。为此,您调用 startActivityForResult(Intent, int)
方法,其中整数参数标识调用。
此标识符旨在区分来自同一 Activity 的对 startActivityForResult(Intent, int)
的多个调用。它不是全局标识符,并且没有与其他应用程序或 Activity 冲突的风险。结果通过您的 onActivityResult(int, int, Intent)
方法返回。
当子 Activity 退出时,它可以调用 setResult(int)
将数据返回给其父级。子 Activity 必须提供一个结果代码,该代码可以是标准结果 RESULT_CANCELED
、RESULT_OK
或从 RESULT_FIRST_USER
开始的任何自定义值。
此外,子 Activity 可以选择性地返回一个 Intent
对象,其中包含它想要提供的任何其他数据。父 Activity 使用 onActivityResult(int, int, Intent)
方法以及父 Activity 最初提供的整数标识符来接收信息。
如果子 Activity 由于任何原因(例如崩溃)失败,则父 Activity 会收到代码为 RESULT_CANCELED
的结果。
Kotlin
class MyActivity : Activity() { // ... override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { // When the user center presses, let them pick a contact. startActivityForResult( Intent(Intent.ACTION_PICK,Uri.parse("content://contacts")), PICK_CONTACT_REQUEST) return true } return false } override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) { when (requestCode) { PICK_CONTACT_REQUEST -> if (resultCode == RESULT_OK) { // A contact was picked. Display it to the user. startActivity(Intent(Intent.ACTION_VIEW, intent?.data)) } } } companion object { internal val PICK_CONTACT_REQUEST = 0 } }
Java
public class MyActivity extends Activity { // ... static final int PICK_CONTACT_REQUEST = 0; public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { // When the user center presses, let them pick a contact. startActivityForResult( new Intent(Intent.ACTION_PICK, new Uri("content://contacts")), PICK_CONTACT_REQUEST); return true; } return false; } protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == PICK_CONTACT_REQUEST) { if (resultCode == RESULT_OK) { // A contact was picked. Display it to the user. startActivity(new Intent(Intent.ACTION_VIEW, data)); } } } }
协调 Activity
当一个 Activity 启动另一个 Activity 时,它们都会经历生命周期转换。第一个 Activity 停止运行并进入暂停或停止状态,而另一个 Activity 被创建。如果这些 Activity 共享保存到磁盘或其他位置的数据,则务必了解第一个 Activity 在第二个 Activity 创建之前不会完全停止。相反,启动第二个 Activity 的过程与停止第一个 Activity 的过程重叠。
生命周期回调的顺序是明确定义的,尤其是在两个 Activity 位于同一进程(换句话说,同一应用程序)中并且一个正在启动另一个 Activity 时。以下是 Activity A 启动 Activity B 时发生的操作顺序
- Activity A 的
onPause()
方法执行。 - Activity B 的
onCreate()
、onStart()
和onResume()
方法按顺序执行。Activity B 现在拥有用户焦点。 - 如果 Activity A 在屏幕上不再可见,则其
onStop()
方法执行。
此生命周期回调序列允许您管理从一个 Activity 到另一个 Activity 的信息转换。