【Android】Activity 的生命周期和启动模式
文章目录
- Activity 的生命周期和启动模式
- 1. 任务和返回栈
- 2. Activity 的四种状态
- 2.1 运行状态
- 2.2 暂停状态
- 2.3 停止状态
- 2.4 销毁状态
- 3. Activity的生命周期
- 3.1 生命周期回调方法
- 3.2 演示 Activity 的生命周期
- 4. Activity的启动模式
- 4.1 standard(标准模式)
- 4.2 singleTop(栈顶复用模式)
- 4.3 singleTask(栈内复用模式)
- 4.4 singleInstance(单实例模式)
- 4.5 使用 Intent Flags
Activity 的生命周期和启动模式
1. 任务和返回栈
《第一行代码》原文如下:
Android 是使用任务(Task)来管理活动(Activity)的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)。栈是一种后进先出的数据结构,在默认情况下,每当我们启动一个新的活动,他会在返回栈入栈,并处于栈顶的位置。而每当我们按下 Back 键或调用 finish() 方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。
下图展示了返回栈是如何管理活动入栈出栈操作的:
返回栈就像一个“页面历史记录”,记录了用户打开的 Activity 的顺序;任务是一个运行中应用组件的集合,包含一个返回栈。
2. Activity 的四种状态
在Android开发中,活动状态(Activity Status)指的是一个活动在其生命周期中的不同状态。主要有以下几种状态:
- 运行状态(Running):活动在屏幕前景中显示,用户可以与之交互。
- 暂停状态(Paused):活动仍然可见,但失去焦点,用户无法与之交互。
- 停止状态(Stopped):活动完全不可见,但仍然保留其状态和成员信息。
- 销毁状态(Destroyed):活动被系统销毁,释放资源。
这些状态的管理对于提升应用的性能和用户体验至关重要。
2.1 运行状态
当一个活动位于返回栈的栈顶时,这时活动就处于运行状态。系统最不愿回收的就是处于运行状态的活动,因为这会带来非常差的用户体验。
2.2 暂停状态
当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。你可能会觉得既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一个活动都会占满整个屏幕的,比如对话框形式的活动只会占用屏幕中间的部分区域,你很快就会在后面看到这种活动。处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种活动。
2.3 停止状态
当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。
2.4 销毁状态
当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机的内存充足。
3. Activity的生命周期
在 Android 系统中,Activity
是用户界面的基本构建块,代表一个单一的屏幕。用户与应用交互时(如打开新屏幕、返回、切换到其他应用、屏幕旋转等),Activity 会经历一系列状态变化。Android 系统通过回调方法通知 Activity 这些状态变化,开发者需要覆盖这些方法来管理资源、保存数据、提供流畅的用户体验。
回调方法:当 Activity 在状态间转换时,系统会自动调用的方法(如
onCreate()
,onStart()
)。开发者重写这些方法来执行特定操作。
3.1 生命周期回调方法
Activity 类中定义了7 个回调方法:
onCreate()
- 触发时机: Activity 首次创建时调用。这是生命周期中必须实现的方法。
- 主要职责:
- 执行基本应用启动逻辑(只应执行一次)。
- 调用
setContentView()
来设置 Activity 的布局。 - 初始化关键的、生命周期感知的组件(如
ViewModel
)。 - 绑定数据到 UI(如果数据已准备好)。
- 下一个状态:
Started
(系统紧接着调用onStart()
)。
onStart()
- 触发时机: Activity 变得可见给用户时调用(但可能不在前台,例如被一个非全屏对话框或另一个透明 Activity 部分覆盖)。在
onCreate()
之后或 Activity 从Stopped
状态恢复时调用。 - 主要职责:
- 准备让 Activity 进入前台并与用户交互所需的资源(这些资源在
onStop()
中释放)。 - 注册需要响应 Activity 可见性的监听器(如广播接收器、位置更新监听器)。
- 启动 UI 更新所需的动画或线程。
- 准备让 Activity 进入前台并与用户交互所需的资源(这些资源在
- 下一个状态:
Resumed
(如果 Activity 进入前台) 或Stopped
(如果 Activity 被完全隐藏)。
onResume()
- 触发时机: Activity 位于栈顶并获得用户焦点时调用。用户正在与 Activity 交互。
- 主要职责:
- 启动或恢复需要用户交互的组件(如摄像头、高精度位置更新、传感器、动画、独占的音频/视频资源)。
- 确保 UI 是最新的(数据可能在上次暂停后发生了变化)。
- 下一个状态:
Paused
(当 Activity 失去焦点但仍部分可见时)。
onPause()
- 触发时机: Activity 失去焦点但仍部分可见时调用(例如,被一个非全屏对话框、透明 Activity 或系统 UI 如权限对话框部分覆盖)。或者当 Activity 即将进入
Stopped
状态时(系统会在onStop()
之前调用onPause()
)。 - 主要职责:
- 释放或调整在
onResume()
中获取的、不需要在暂停状态下运行的独占资源(如摄像头、传感器、高精度定位、独占音频焦点)。 - 提交未保存的更改(但轻量级的,因为系统可能很快调用
onResume()
)。不建议执行 CPU 密集型操作或持久化数据(如数据库写入),这会延迟到下一个 Activity 的转换。 - 为进入
Stopped
状态做准备。
- 释放或调整在
- 下一个状态:
Resumed
(如果用户返回到该 Activity) 或Stopped
(如果 Activity 被完全隐藏)。
- 触发时机: Activity 失去焦点但仍部分可见时调用(例如,被一个非全屏对话框、透明 Activity 或系统 UI 如权限对话框部分覆盖)。或者当 Activity 即将进入
onStop()
- 触发时机: Activity 对用户完全不可见时调用(例如,被另一个 Activity 完全覆盖、用户切换到其他应用、或 Activity 本身即将被销毁)。
- 主要职责:
- 释放或调整所有在
onStart()
中获取的、不需要在不可见状态下运行的资源(如广播接收器、位置更新监听器、UI 动画线程)。 - 执行 CPU 相对密集的关闭操作(如将数据保存到数据库)。
- 保存当前的 UI 状态(通过
onSaveInstanceState()
)。
- 释放或调整所有在
- 下一个状态:
Restarted
(如果 Activity 重新变得可见) 或Destroyed
(如果系统需要回收内存或用户显式关闭 Activity)。
onDestroy()
- 触发时机: Activity 即将被销毁或已完成销毁时调用。这可能发生在:
- 用户显式关闭 Activity(如按返回键)。
- 在 Activity 处于
Stopped
状态且系统需要回收内存时被系统终止。 - 由于配置变更(如屏幕旋转、语言更改)系统临时销毁该 Activity 实例(紧接着会创建一个新的实例)。
- 主要职责:
- 执行最终的清理工作(如注销全局监听器、释放可能泄漏的资源)。
- 确保所有在
onCreate()
中创建并长期运行的操作(如网络请求)被取消或清理。 - 区分是永久销毁还是配置变更导致的临时销毁(检查
isFinishing()
)。
onRestart()
- 在 Activity 从
Stopped
状态重新回到Started
状态之前调用(即在onStart()
之前)。 - 用于执行仅在 Activity 从停止状态恢复时才需要的特殊初始化(不同于首次创建的初始化)。
- 在 Activity 从
下图是 Android 官方提供的一张活动生命周期示意图:
这七种方法除了 onRestart() 方法,其他都是两两相对的,从而又可将活动分为 3 种生存期:
- 完整生存期
- 范围:
onCreate()
➔onDestroy()
- 核心特点:
- 覆盖 Activity 从创建到销毁的完整过程
onCreate()
完成全局初始化(布局、数据绑定等)onDestroy()
执行最终清理(释放内存、关闭连接等)
- 范围:
- 可见生存期
- 范围:
onStart()
➔onStop()
- 核心特点:
- Activity 对用户可见的整个阶段
- 包括前台(可交互)和后台(被部分遮挡)状态
- 资源管理原则:可见时加载,不可见时释放
- 范围:
- 前台生存期
- 范围:
onResume()
➔onPause()
- 核心特点:
- Activity 处于栈顶且可交互的状态
- 用户直接操作的黄金时期
- 响应速度要求最高(禁止耗时操作)
- 范围:
《第一行代码》原文:
Activity 类中定义了 7 个回调方法,覆盖了活动生命周期的每一个环节,喜爱按就来一一介绍这 7 个方法。
onCreate()
它会在活动第一次创建的时候调用。应该在这个方法中完成活动的初始化操作,比如加载布局、绑定事件等。onStart()
这个方法在活动由不可见变为可见的时候调用。onResume()
这个方法在活动准备好和用户进行交互的时候调用。此时的活动一定位于返回栈的栈顶,并且处于运行状态。onPause()
这个方法在系统准备去启动或者恢复另一个活动的时候调用。通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。onDestory()
这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。onRestart()
这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。以上 7 个方法除了 onRestart() 方法,其他都是两两相对的,从而又可将活动分为 3 种生存期。
完整生存期
活动在 onCreate() 方法和 onDestroy() 方法之间所经历的就是完整生存期。一般情况下,一个活动会在 onCreate() 方法中完成各种初始化操作,而在 onDestroy() 方法中完成释放内存的操作。
可见生存期
活动在 onStart() 方法和 onStop() 方法之间所经历的就是可见生存期。 在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互。我们可以通过这两个方法,合理地管理那些对用户可见的资源。比如在 onStart() 方法中对资源进行加载,而在 onStop() 方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。
前台生存期
活动在 onResume() 方法和 onPause() 方法之间所经历的就是前台生存期。 在前台生存期内,活动总是处于运行状态的,此时的活动是可以和用户进行交互的,我们平时看到和接触最多的也就是这个状态下的活动。
Activity 生命周期回调方法总结
回调方法 | 触发时机 | 核心用途 | 下一状态 |
---|---|---|---|
onCreate() | Activity 首次创建时调用 | 1. 初始化基本组件 2. 调用 setContentView() 绑定布局 3. 初始化数据(如 ViewModel) | onStart() |
onStart() | Activity 对用户可见时 | 1. 注册与可见性相关的监听器 2. 准备前台显示所需资源 | onResume() 或 onStop() |
onResume() | Activity 进入前台并获得焦点 | 1. 启动用户交互组件(如动画、传感器) 2. 刷新 UI 数据 | onPause() |
onPause() | Activity 失去焦点但仍部分可见 | 1. 释放独占资源(如相机) 2. 提交轻量级数据保存 | onResume() 或 onStop() |
onStop() | Activity 完全不可见时 | 1. 释放所有可见性相关资源 2. 保存重要数据到持久存储 3. 调用 onSaveInstanceState() | onRestart() 或 onDestroy() |
onRestart() | Activity 从停止状态重新显示前 | 执行恢复可见时的特殊初始化 | onStart() |
onDestroy() | Activity 销毁前(永久或临时) | 1. 清理所有资源 2. 终止后台任务 3. 区分 isFinishing() | 无 |
注:以上所有方法均可通过重写(
@Override
)在自定义 Activity 中实现。
3.2 演示 Activity 的生命周期
以下是一个简单的 Android 活动生命周期演示代码,通过日志输出清晰展示各个生命周期方法的触发时机:
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {// 日志标签private static final String TAG = "LifecycleDemo";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d(TAG, "onCreate - 活动被创建");// 添加关闭按钮点击事件findViewById(R.id.close_button).setOnClickListener(v -> {Log.d(TAG, "用户点击关闭按钮");finish(); // 结束活动});}@Overrideprotected void onStart() {super.onStart();Log.d(TAG, "onStart - 活动可见");}@Overrideprotected void onResume() {super.onResume();Log.d(TAG, "onResume - 活动可交互");}@Overrideprotected void onPause() {super.onPause();Log.d(TAG, "onPause - 活动失去焦点");}@Overrideprotected void onStop() {super.onStop();Log.d(TAG, "onStop - 活动不可见");}@Overrideprotected void onRestart() {super.onRestart();Log.d(TAG, "onRestart - 活动重新启动");}@Overrideprotected void onDestroy() {super.onDestroy();Log.d(TAG, "onDestroy - 活动被销毁");}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:gravity="center"android:padding="16dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="活动生命周期演示"android:textSize="20sp"android:textStyle="bold"/><TextViewandroid:id="@+id/lifecycle_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="查看Logcat中的生命周期日志"android:textSize="18sp"android:layout_marginTop="24dp"/><Buttonandroid:id="@+id/close_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="关闭活动"android:layout_marginTop="24dp"/></LinearLayout>
初始启动应用
按下返回键
点击“关闭活动”按钮
按下 Home 键返回桌面
从最近任务重新打开应用
旋转屏幕(测试配置变更)
4. Activity的启动模式
Android 的活动启动模式决定了活动实例如何与任务栈交互,是 Android 系统管理活动实例的重要机制,在实际项目中我们应根据特定的需求为每个活动指定恰当的启动模式。启动模式一共有四种:standard、singleTop、singleTask 和 singleInstance,可以在 AndroidManifest.xml 中通过给<activity>
标签指定 android:launchMode
属性来选择启动模式。
示例代码:
<activityandroid:name=".MainActivity"android:exported="true"android:launchMode="singleTop">
4.1 standard(标准模式)
《第一行代码》原文:
standard 是活动默认的启动模式,在不进行显式指定的情况下,所有活动都会自动使用这种启动模式。因此,到目前为止我们写过的所有活动都是使用的standard模式。经过上一节的学习,你已经知道了 Android 是使用返回栈来管理活动的,在standard模式(即默认情况)下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置。对于使用 standard 模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
- 默认模式,每次启动活动都会创建新实例。
- 新实例被压入启动它的任务栈栈顶
- 适用场景:普通活动,需要多个实例的情况
4.2 singleTop(栈顶复用模式)
《第一行代码》原文:
可能在有些情况下,你会觉得 standard 模式不太合理。活动明明已经在栈顶了,为什么再次启动的时候还要创建一个新的活动实例呢?别着急,这只是系统默认的一种启动模式而已,你完全可以根据自己的需要进行修改,比如说使用 singleTop 模式。当活动的启动模式指定为 singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
- 如果目标活动已在栈顶,则复用实例(调用
onNewIntent()
) - 不在栈顶则创建新实例
- 适用场景:防止重复创建栈顶活动(如通知跳转)
4.3 singleTask(栈内复用模式)
《第一行代码》原文:
使用 single Top 模式可以很好地解决重复创建栈顶活动的问题,但是正如你在上一节所看到的,如果该活动并没有处于栈顶的位置,还是可能会创建多个活动实例的。那么有没有什么办法可以让某个活动在整个应用程序的上下文中只存在一个实例呢?这就要借助 single Task 模式来实现了。当活动的启动模式指定为singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
- 系统会寻找匹配的任务栈(由
taskAffinity
决定) - 如果存在实例,则清除其上的所有活动并调用
onNewIntent()
- 不存在则创建新实例
- 适用场景:应用入口活动(如主界面)
4.4 singleInstance(单实例模式)
《第一行代码》原文:
singleInstance 模式应该算是 4 种启动模式中最特殊也最复杂的一个了,你也需要多花点功夫来理解这个模式。不同于以上 3 种启动模式,指定为 singleInstance 模式的活动会启用一个新的返回栈来管理这个活动(其实如果singleTask 模式指定了不同的taskAffinity,也会启动一个新的返回栈)。那么这样做有什么意义呢?想象以下场景,假设我们的程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,应该如何实现呢?使用前面 3 种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例。而使用 singleInstance 模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。
- 活动独占一个任务栈
- 全局唯一实例
- 后续启动直接复用该实例
- 适用场景:需要全局共享的活动(如来电界面)
taskAffinity
: 这个属性通常与singleTask
和singleInstance
结合使用,用于指定Activity
希望归属的任务栈的名称(默认为应用包名)。它影响singleTask
寻找或创建哪个专属栈,以及Intent
标志FLAG_ACTIVITY_NEW_TASK
的行为。onNewIntent()
: 对于复用实例的模式 (singleTop
,singleTask
,singleInstance
),当复用发生时,新的Intent
是通过onNewIntent()
方法传递的,而不是onCreate()
。务必在此方法中处理新的意图数据。
4.5 使用 Intent Flags
启动模式也可以动态指定,优先级高于清单中声明:
Intent intent = new Intent(this, TargetActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
常用标志:
标志 | 说明 |
---|---|
FLAG_ACTIVITY_NEW_TASK | 在新任务中启动 Activity |
FLAG_ACTIVITY_SINGLE_TOP | 等同于 singleTop |
FLAG_ACTIVITY_CLEAR_TOP | 清除目标 Activity 之上的所有 Activity |
FLAG_ACTIVITY_CLEAR_TASK | 在启动前清除整个任务的所有 Activity |
FLAG_ACTIVITY_REORDER_TO_FRONT | 如果 Activity 已存在,将其移到栈顶 |