了解Activity生命周期

参考资料

前言

当用户浏览、退出和返回到一个应用时,该应用中的Activity实例会在其生命周期的不同状态之间转换。Activity类会提供许多回调,这些回调会让Activity知道某个状态已经更改:系统正在创建、停止或恢复某个Activity,或者正在销毁该Activity所在的进程。

在生命周期回调方法中,可以声明用户离开和再次进入Activity时Activity的行为方式。例如,如果您正构建流媒体视频播放器,当用户切换至另一应用时,您可能要暂停视频或终止网络连接。当用户返回时,您可以重新连接网络并允许用户从同一位置继续播放视频。换言之,每个回调都支持执行特定状态下需要的工作任务。在合适的时间执行正确的工作,将可以提升应用的稳健性和性能。例如,良好的生命周期回调实现有助于防止应用出现以下问题:

  • 当用户在使用应用时接听来电,或切换至另一应用时崩溃。
  • 当用户未主动使用它时,消耗宝贵的系统资源。
  • 当用户离开应用并在稍后返回时,丢失用户的进度。
  • 当屏幕在横向和纵向之间旋转时,崩溃或丢失用户的进度。

Activity生命周期概念

为了在 Activity 生命周期的各个阶段之间导航转换,Activity 类提供六个核心回调:onCreate()、onStart()、onResume()、onPause()、onStop() 和 onDestroy()。当 Activity 进入新状态时,系统会调用其中每个回调。
在这里插入图片描述当用户开始离开Activity时,系统会调用方法来销毁该Activity。在某些情况下,此销毁只是部分销毁;Activity 仍然驻留在内存中(例如当用户切换至另一应用时),并且仍然可以返回到前台。如果用户返回到该 Activity,Activity 会从用户离开时的位置继续运行。

生命周期回调

onCreate()

此回调必须实现,它会在系统首次创建Activity时触发。Activity会在创建后进入“已创建”状态。该方法在Activity生命周期中只应发生一次。可以在该方法中进行一些数据绑定,变量初始化的工作。此方法接收savedInstanceState 参数,后者是包含 Activity 先前保存状态的 Bundle 对象。如果 Activity 此前未曾存在,Bundle 对象的值为 null。

如果有一个生命周期感知型组件与Activity的生命周期像关联,该组件将收到ON_CREATE事件。系统将调用带有@OnLifecycleEvent注释的方法,以使生命周期感知型组件可以执行已创建状态所需的任何设置代码。

onCreate()方法执行完成之后,activity进入“已开始”状态,并相继调用onStart()和onResume()方法。

onStart()

当 Activity 进入“已开始”状态时,系统会调用此回调。onStart() 调用使 Activity 对用户可见,因为应用会为 Activity 进入前台并支持互动做准备。例如,应用通过此方法来初始化维护界面的代码。

当 Activity 进入已开始状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_START 事件。

onStart() 方法会非常快速地完成,并且与“已创建”状态一样,Activity 不会一直处于“已开始”状态。一旦此回调结束,Activity 便会进入“已恢复”状态,系统将调用 onResume() 方法。

onResume()

Activity 会在进入“已恢复”状态时来到前台,然后系统调用 onResume() 回调。这是应用与用户互动的状态。应用会一直保持这种状态,直到某些事件发生,让焦点远离应用。此类事件包括接到来电、用户导航到另一个 Activity,或设备屏幕关闭。

当 Activity 进入已恢复状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_RESUME 事件。这时,生命周期组件可以启用在组件可见且位于前台时需要运行的任何功能,例如启动相机预览。

当发生中断事件时,Activity 进入“已暂停”状态,系统调用 onPause() 回调。

如果 Activity 从“已暂停”状态返回“已恢复”状态,系统将再次调用 onResume() 方法。因此,您应实现 onResume(),以初始化在 onPause() 期间释放的组件,并执行每次 Activity 进入“已恢复”状态时必须完成的任何其他初始化操作。

无论您选择在哪个构建事件中执行初始化操作,都请务必使用相应的生命周期事件来释放资源。如果您在收到 ON_START 事件后初始化某些内容,请在收到 ON_STOP 事件后释放或终止相应内容。如果您在收到 ON_RESUME 事件后初始化某些内容,请在收到 ON_PAUSE 事件后将其释放。

onPause()

系统将此方法视为用户将要离开您的 Activity 的第一个标志(尽管这并不总是意味着 Activity 会被销毁);此方法表示 Activity 不再位于前台(尽管在用户处于多窗口模式时 Activity 仍然可见)。使用 onPause() 方法暂停或调整当 Activity 处于“已暂停”状态时不应继续(或应有节制地继续)的操作,以及您希望很快恢复的操作。Activity 进入此状态的原因有很多。例如:

  • 如 onResume() 部分所述,某个事件会中断应用执行。这是最常见的情况。
  • 在 Android 7.0(API 级别 24)或更高版本中,有多个应用在多窗口模式下运行。无论何时,都只有一个应用(窗口)可以拥有焦点,因此系统会暂停所有其他应用。
  • 有新的半透明 Activity(例如对话框)处于开启状态。只要 Activity 仍然部分可见但并未处于焦点之中,它便会一直暂停。

当 Activity 进入已暂停状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_PAUSE 事件。

您还可以使用 onPause() 方法释放系统资源、传感器(例如 GPS)手柄,或当您的 Activity 暂停且用户不需要它们时仍然可能影响电池续航时间的任何资源。然而,正如上文的 onResume() 部分所述,如果处于多窗口模式,“已暂停”的 Activity 仍完全可见。因此,您应该考虑使用 onStop() 而非 onPause() 来完全释放或调整与界面相关的资源和操作,以便更好地支持多窗口模式。

onPause() 执行非常简单,而且不一定要有足够的时间来执行保存操作。因此,您不应使用 onPause() 来保存应用或用户数据、进行网络调用或执行数据库事务。因为在该方法完成之前,此类工作可能无法完成。相反,您应在 onStop() 期间执行高负载的关闭操作。

onPause() 方法的完成并不意味着 Activity 离开“已暂停”状态。相反,Activity 会保持此状态,直到其恢复或变成对用户完全不可见。如果 Activity 恢复,系统将再次调用 onResume() 回调。如果 Activity 从“已暂停”状态返回“已恢复”状态,系统会让 Activity 实例继续驻留在内存中,并会在系统调用 onResume() 时重新调用该实例。在这种情况下,您无需重新初始化在任何回调方法导致 Activity 进入“已恢复”状态期间创建的组件。如果 Activity 变为完全不可见,系统会调用 onStop()。

onStop()

如果您的 Activity 不再对用户可见,说明其已进入“已停止”状态,因此系统将调用 onStop() 回调。例如,当新启动的 Activity 覆盖整个屏幕时,可能会发生这种情况。如果 Activity 已结束运行并即将终止,系统还可以调用 onStop()。

当 Activity 进入已停止状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_STOP 事件。这时,生命周期组件可以停止在组件未显示在屏幕上时无需运行的任何功能。

在 onStop() 方法中,应用应释放或调整在应用对用户不可见时的无用资源。例如,应用可以暂停动画效果,或从精确位置更新切换到粗略位置更新。使用 onStop() 而非 onPause() 可确保与界面相关的工作继续进行,即使用户在多窗口模式下查看您的 Activity 也能如此。

您还应使用 onStop() 执行 CPU 相对密集的关闭操作。例如,如果您无法找到更合适的时机来将信息保存到数据库,可以在 onStop() 期间执行此操作。

当您的 Activity 进入“已停止”状态时,Activity 对象会继续驻留在内存中:该对象将维护所有状态和成员信息,但不会附加到窗口管理器。Activity 恢复后,Activity 会重新调用这些信息。您无需重新初始化在任何回调方法导致 Activity 进入“已恢复”状态期间创建的组件。系统还会追踪布局中每个 View 对象的当前状态,如果用户在 EditText 微件中输入文本,系统将保留文本内容,因此您无需保存和恢复文本。

注意:Activity 停止后,如果系统需要恢复内存,可能会销毁包含该 Activity 的进程。即使系统在 Activity 停止后销毁相应进程,系统仍会保留 Bundle(键值对的 blob)中 View 对象(例如 EditText 微件中的文本)的状态,并在用户返回 Activity 时恢复这些对象

进入“已停止”状态后,Activity 要么返回与用户互动,要么结束运行并消失。如果 Activity 返回,系统将调用 onRestart()。如果 Activity 结束运行,系统将调用 onDestroy()。

onDestroy()

销毁 Ativity 之前,系统会先调用 onDestroy()。系统调用此回调的原因如下:

  1. Activity 即将结束(由于用户彻底关闭 Activity 或由于系统为 Activity 调用 finish())
  2. 由于配置变更(例如设备旋转或多窗口模式),系统暂时销毁 Activity

当 Activity 进入已销毁状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_DESTROY 事件。这时,生命周期组件可以在 Activity 被销毁之前清理所需的任何数据。

您应使用 ViewModel 对象来包含 Activity 的相关视图数据,而不是在您的 Activity 中加入逻辑来确定 Activity 被销毁的原因。如果因配置变更而重新创建 Activity,ViewModel 不必执行任何操作,因为系统将保留 ViewModel 并将其提供给下一个 Activity 实例。如果不重新创建 Activity,ViewModel 将调用 onCleared() 方法,以便在 Activity 被销毁前清除所需的任何数据。

可以使用 isFinishing() 方法区分这两种情况。

如果 Activity 即将结束,onDestroy() 是 Activity 收到的最后一个生命周期回调。如果由于配置变更而调用 onDestroy(),系统会立即新建 Activity 实例,然后在新配置中为新实例调用 onCreate()。

onDestroy() 回调应释放先前的回调(例如 onStop())尚未释放的所有资源。

Activity状态和从内存中弹出

系统会在需要释放RAM时终止进程;系统终止给定进程的可能性取决于当时进程的状态。

系统终止进程的可能性进程状态Activity状态
较小前台(拥有或即将获得焦点)已创建/已开始/已恢复
较大后台(失去焦点)已暂停
最大后台(不可见)已停止
最大已销毁

系统永远不会直接终止Activity以释放内存,而是会终止Activity所在的进程。系统不仅会销毁Activity,还会销毁在该进程中运行的所有其他内容。

保存和恢复瞬时界面状态

用户期望Activity的界面状态在整个配置变更(例如旋转或切换到多窗口模式)期间保持不变。但是,默认情况下,系统会在发生此类配置更改时销毁Activity,从而清除存储在Activity实例中的任何界面状态。同样,如果用户暂时从当前应用切换到其他应用,并在稍后返回,他们也希望界面状态保持不变。但是,当用户离开应用且Activity停止时,系统可能会销毁该应用的进程。

当activity因为系统限制而销毁时,应组合使用 ViewModel、onSaveInstanceState() 和/或本地存储来保留用户的瞬时界面状态。

本部分概述了实例状态的定义,以及如何实现onSaveInstance()方法,该方法是对Activity本身的回调。如果界面数据简单轻量化,例如原始数据类型或简单对象,可以单独使用onSaveInstanceState()使界面状态在配置更改和进程被终止时保持不变。但大多数情况下,应使用ViewModel 和 onSaveInstanceState(),因为 onSaveInstanceState() 会产生序列化/反序列化成本。

实例状态

在某些情况下,Activity会因为正常的应用行为而被销毁。但是,如果系统因为一些限制(如配置变更或内存压力)而销毁Activity,虽然实际的Activity实例会消失,但系统会记住它曾经存在过。如果用户尝试回到该Activity,系统将使用一组描述Activity销毁时状态的已保存数据新建该Activity的实例。

系统用户恢复先前状态的已保存数据称为实例状态,以键值对集合存储于Bundle对象中。默认情况下,系统使用Bundle实例状态来保存Activity布局中每个View对象的相关信息。

注意:为了使 Android 系统恢复 Activity 中视图的状态,每个视图必须具有 android:id 属性提供的唯一 ID。

Bundle对象不适合保存大量数据,因为它需要在主线程上进行序列化处理并占用系统进程内存。如需保存大量数据,您应组合使用持久性本地存储、onSaveInstanceState() 方法和 ViewModel 类来保存数据。

使用onSaveInstanceState()保存简单轻量的界面状态

当您的 Activity 开始停止时,系统会调用 onSaveInstanceState() 方法,以便您的 Activity 可以将状态信息保存到实例状态 Bundle 中。此方法的默认实现保存有关 Activity 视图层次结构状态的瞬时信息。

如需保存Activity的其他实例状态信息,必须替换onSaveInstanceState(),并将键值对添加到Bundle对象中。替换 onSaveInstanceState() 时,如果您希望默认实现保存视图层次结构的状态,必须调用父类实现。

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界面状态

重建先前被销毁的Activity后,可以从系统传递给Activity的Bundle中恢复保存的实例状态。onCreate()和onRestoreInstanceState()回调方法均会收到包含实例状态信息的相同Bundle。

因为不论系统是新建Activity实例还是重写创建之前的实例,都会调用onCreate()方法,所以在尝试读取之前,必须检查状态Bundle是否null。如果是null,系统将新建Activity实例,而不会恢复之前被销毁的实例。

@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
    }
    // ...
}

可以选择实现系统在onStart()方法之后调用的onRestoreInstanceState(),而不是在onCreate()期间恢复状态。仅当存在要恢复的已保存状态时,系统才会调用onRestoreInstanceState(),因此无需检查Bundle是否为null。

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使用的少量数据。

startActivity()

如果新启动的Activity不需要返回结果,当前Activity可以通过调用startActivity()方法来启动它。在自己的应用中工作时,通常只需要启动已知Activity。
某些时候,应用可能还希望使用Activity中的数据执行某些操作,例如发送电子邮件、短信或状态更新。在这种情况下,可能需要使用设备上其他应用提供的Activity来执行这些工作。这便是Intent存在的价值。可以创建一个intent,对想执行的操作进行描述,系统会从其他应用启动柜相应的Activity。如果有多个Activity可以处理intent,用户可以选择要使用哪一个。例如,如果想使用发送电子邮件功能,可以创建以下intent:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

添加到intent中的EXTRA_EMAIL是一个字符串组,其中包含电子邮件的收件人电子邮件地址。当电子邮件应用响应此intent,该应用会读取extra中提供的字符串数组,并将该数组放入电子邮件撰写表单的“收件人”字段。在这种情况下,电子邮件应用的Activity会启动,并且当用户完成操作时,您的Activity会继续运行。

startActivityForResult()

有时,会希望在Activity结束时从Activity中获取返回结果。例如,您可以启动一项Activity,让用户在联系人列表中选择收件人;当Activity结束时,系统将返回用户选择的收件人。为此,您可以调用startActivityForResult(Intent, int) 方法,其中整数参数会标识该调用。此标识符用于消除来自同一Activity的多次startActivityForResult(Intent, int) 调用之间的歧义。这不是全局标识符,不存在与其他应用或Activity冲突的风险。结果通过 onActivityResult(int, int, Intent) 方法返回

当子级Activity退出时,它可以调用setResult(int)将数据返回到其父级。子级Activity必须始终提供结果代码,该结果代码可以是标准结果RESULT_CANCELEDRESULT_OK,也可以是从RESULT_FIRST_USER开始的任何自定义值。此外,子级Activity可以根据需要返回包含它所需的任何其他数据的Intent对象。父级Activity使用onActivityResult(int, int, Intent) 方法,以及父级 Activity 最初提供的整数标识符来接收信息。

如果子级Activity由于任何原因(例如崩溃)而失败,父级Activity将收到代码为RESULT_CANCELED的结果。

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.  Here we will just display it
                 // to the user.
                 startActivity(new Intent(Intent.ACTION_VIEW, data));
             }
         }
     }
 }

协调Activity

当一个 Activity 启动另一个 Activity 时,它们都会经历生命周期转换。第一个 Activity 停止运行并进入“已暂停”或“已停止”状态,同时创建另一个 Activity。如果这些 Activity 共享保存到磁盘或其他位置的数据,必须要明确第一个 Activity 在创建第二个 Activity 之前并未完全停止。相反,启动第二个 Activity 的过程与停止第一个 Activity 的过程重叠。

生命周期回调的顺序已有明确定义,特别是当两个 Activity 在同一个进程(应用)中,并且其中一个要启动另一个时。以下是 Activity A 启动 Activity B 时的操作发生顺序:

  1. Activity A 的 onPause() 方法执行。
  2. Activity B 的 onCreate()、onStart() 和 onResume() 方法依次执行(Activity B 现在具有用户焦点)。
  3. 然后,如果 Activity A 在屏幕上不再显示,其 onStop() 方法执行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值