【磨叽教程】Android进阶教程之Activity生命周期详解

了解Activity生命周期

【磨叽教程】Android进阶教程之Activity生命周期详解_第1张图片

在我们正常使用某一个APP的过程中,通常是在这个应用中的不同界面直接来回切换。相对应的Activity实例会在其生命周期的不同状态变换。

Activity提供了很多回调,每个回调都表示个某种状态。比如,我们从打开一个界面到关闭这个界面,Activity就经历了从创建、准备、停止或恢复、停止到销毁一系列的状态变换。

在生命周期回调方法中,我们可以控制用户离开和再次进入 Activity 时 Activity 的行为方式。例如,如果您正构建流媒体视频播放器,当用户切换至另一应用时,您可能要暂停视频或终止网络连接。当用户返回时,您可以重新连接网络并允许用户从同一位置继续播放视频。

换言之,每个回调都可以帮助我们在合适的时间执行正确的任务,并妥善处理转换,这将提升应用的稳健性和性能。

例如,良好的生命周期回调实现有助于防止应用出现以下问题:

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

本文将详细介绍 Activity 生命周期。首先介绍生命周期概念,接着介绍每个回调:它们执行时内部发生了什么,以及我们应该在执行期间实现什么。然后,简要介绍 Activity 状态与导致进程被系统终止的漏洞之间的关系。更多详情请关注“计算机自学平台”公众号。

Activity 生命周期概念

为了解决Activity与Activity之间的各种转换而保证系统的完整性、性能和正确性,Activity 类提供六个核心回调:onCreate()、onStart()、onResume()、onPause()、onStop()onDestroy()。当创建一个新的Activity时,系统会调用其中每个回调。

【磨叽教程】Android进阶教程之Activity生命周期详解_第2张图片
图 1. Activity 生命周期的简化图示。

当用户开始离开 Activity 时,系统会调用方法来销毁该 Activity。在某些情况下,此销毁只是部分销毁;Activity 仍然驻留在内存中(例如当用户切换至另一应用时),并且仍然可以返回到前台。如果用户返回到该 Activity,Activity 会从用户离开时的位置继续运行。
除了少数例外,应用在后台运行时会受到限制,无法启动 Activity。
根据 Activity 的复杂程度,我们在开发的时候不一定要实现Activity的每一个生命周期方法。但是,请务必了解每个方法,并确认我们是在对的时间做对的事情,这非常重要

生命周期回调

本部分介绍 Activity 生命周期中所有回调方法的相关概念及相关信息。

onCreate()

每一个Activity中都必须实现此回调,它会在系统首次创建 Activity 时触发。Activity 会在创建后进入“已创建”状态。在 onCreate() 方法中,我们需执行基本应用启动逻辑,该逻辑在 Activity 的整个生命周期中只应执行一次。
例如,将 Activity 与 ViewModel 相关联,并实例化某些类作用域变量。此方法会接收 savedInstanceState 参数,后者是包含 Activity 先前保存状态的 Bundle 对象。如果 Activity 此前未曾存在,Bundle 对象的值为 null。

以下示例是在onCreate() 方法中执行 Activity 某些基本设置的一些代码,例如声明界面(在 XML 布局文件中定义)、定义成员变量,以及配置某些界面。在这个示例中,系统通过将文件的资源 ID R.layout.main_activity 传递给 setContentView() 来指定 XML 布局文件。

TextView textView;

// some transient state for the activity instance
String gameState;

@Override
public void onCreate(Bundle savedInstanceState) {
    // call the super class onCreate to complete the creation of activity like
    // the view hierarchy
    super.onCreate(savedInstanceState);

    // recovering the instance state
    if (savedInstanceState != null) {
        gameState = savedInstanceState.getString(GAME_STATE_KEY);
    }

    // set the user interface layout for this activity
    // the layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity);

    // initialize member TextView so we can manipulate it later
    textView = (TextView) findViewById(R.id.text_view);
}

// This callback is called only when there is a saved instance that is previously saved by using
// onSaveInstanceState(). We restore some state in onCreate(), while we can optionally restore
// other state 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 may 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()外,您还可以在 Activity 代码中新建 View 对象,并将新建的 View 插入到 ViewGroup 中,以构建视图层次结构。然后,将根 ViewGroup 传递给 setContentView() 以使用该布局。

onCreate() 方法完成执行后,您的 Activity 现在处于“已创建”状态。

onStart()

当 Activity 进入“已开始”状态时,系统会调用此回调。onStart() 调用后使 Activity 将对用户可见,因为应用会为 Activity 进入前台并支持互动做准备。

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

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

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

如果 Activity 从“已暂停”状态重新返回“已恢复”状态,系统将再次调用 onResume() 方法。因此,我们应在 *onResume()*中实现Activity每次进入该状态时所必需的的所有组件和数据的初始化操作。
然而,在多窗口模式下,即使处于“已暂停”状态,您的 Activity 也可能完全可见。

无论选择在哪个构建事件中执行初始化操作,都请务必使用相应的生命周期事件来释放资源。

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 覆盖整个屏幕时。

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

我们还应使用 onStop() 执行 CPU 相对密集的关闭操作。例如,如果您无法找到更合适的时机来将信息保存到数据库,可以在 onStop() 期间执行此操作。以下示例展示了 onStop() 的实现,它将草稿笔记内容保存到持久性存储空间中:

@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 提供抽象层的持久性库。

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

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

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

onDestroy()

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

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

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

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

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

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

你可能感兴趣的:(磨叽教程—Android进阶)