简介
在 Android 应用中,Activity
是用户交互的屏幕载体,其生命周期由系统在不同状态之间回调一系列方法来管理。了解并正确使用这些回调,可确保在屏幕创建、显示、暂停、恢复、销毁等阶段合理分配资源、保存状态和释放内存,从而提升应用的稳定性和用户体验。
1. Activity 生命周期概览
Android 把 Activity
的生命周期分为六大核心状态(Created、Started、Resumed、Paused、Stopped、Destroyed),并通过相应的回调方法让开发者插入自定义逻辑。
- 创建阶段:
onCreate()
→onStart()
→onResume()
,此时Activity
可见并可交互。 - 前台交互:
Resumed
状态下,用户正在与界面交互。此时系统给予最高优先级,若需做动画或获取数据,可在此阶段启动。 - 离开前台:
onPause()
→onStop()
,表示界面不再可交互或完全不可见,此时应暂停动画、保存关键数据、释放占用资源 。 - 销毁阶段:
onDestroy()
,系统或用户调用finish()
、配置变化(如旋转)时触发,应在此彻底清理资源并保存最后状态。
2. 创建与启动
onCreate()
- 调用时机:
Activity
首次被创建时调用,是唯一一个 必需 覆盖的方法 。 - 常用操作:调用
setContentView()
加载布局、初始化视图绑定、恢复持久化数据、注册监听器或依赖注入等。
- 调用时机:
onStart()
- 调用时机:紧接在
onCreate()
之后,或onRestart()
之后恢复到可见状态时触发。 - 常用操作:开始注册广播接收器、启动或恢复不需前台交互的更新(如位置、传感器)。
- 调用时机:紧接在
onResume()
- 调用时机:在
onStart()
之后,Activity
真正进入前台并与用户交互之前触发。 - 常用操作:恢复动画、获取焦点、启动前台服务、恢复摄像头预览或音视频播放等。
- 调用时机:在
3. 暂停与隐藏
onPause()
- 调用时机:当系统准备启动或恢复另一个
Activity
时,当前Activity
离开前台但仍部分可见时调用。 - 常用操作:停止动画、提交编辑器更改、立即保存易丢失的数据,并尽快返回以免影响用户体验。
- 调用时机:当系统准备启动或恢复另一个
onStop()
- 调用时机:当
Activity
完全不可见时调用,如新Activity
覆盖或返回桌面时触发。 - 常用操作:释放占用大量内存的资源(如相机、传感器、位置信息)、注销广播接收器等。
- 调用时机:当
4. 重启与销毁
-
onRestart()
- 调用时机:
Activity
在停止后、再次启动可见前调用,紧接onStop()
与onStart()
之间。 - 常用操作:可在此恢复仅在可见时才需保持的状态,如重新注册监听器。
- 调用时机:
-
onDestroy()
-
调用时机:
Activity
被销毁前调用,可能因调用finish()
或系统为回收资源而临时销毁。 -
常用操作:彻底释放所有资源,注销所有回调,保存最终持久化状态。
-
5. Activity 状态图
6. 生命周期示例Demo
-
BaseActivity
open class BaseActivity : AppCompatActivity() { private val TAG = "Lifecycle" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(TAG, "${this::class.java.simpleName} onCreate") } override fun onStart() { super.onStart() Log.d(TAG, "${this::class.java.simpleName} onStart") } override fun onResume() { super.onResume() Log.d(TAG, "${this::class.java.simpleName} onResume") } override fun onPause() { super.onPause() Log.d(TAG, "${this::class.java.simpleName} onPause") } override fun onStop() { super.onStop() Log.d(TAG, "${this::class.java.simpleName} onStop") } override fun onRestart() { super.onRestart() Log.d(TAG, "${this::class.java.simpleName} onRestart") } override fun onDestroy() { super.onDestroy() Log.d(TAG, "${this::class.java.simpleName} onDestroy") } }
-
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rootLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn_jump" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="跳转 MainActivity2" android:layout_margin="16dp" /> </LinearLayout>
-
MainActivity
class MainActivity : BaseActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // 跳转至 MainActivity2 binding.btnJump.setOnClickListener { startActivity(Intent(this@MainActivity, MainActivity2::class.java)) } } }
-
activity_main2.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity2"> <Button android:id="@+id/btn_finish" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="退出当前界面" /> </LinearLayout>
-
MainActivity2
class MainActivity2 : BaseActivity() { private lateinit var binding: ActivityMain2Binding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMain2Binding.inflate(layoutInflater) setContentView(binding.root) // 结束当前界面 binding.btnFinish.setOnClickListener { finish() } } }
我们初始运行App,通过查看日志我们知道初始启动的时候
MainActivtiy
的生命周期MainActivity onCreate MainActivity onStart MainActivity onResume
我们直接 home 键退出,生命周期如下
MainActivity onPause MainActivity onStop
我们点击按钮跳转至
MainActivity2
,生命周期如下MainActivity onPause MainActivity2 onCreate MainActivity2 onStart MainActivity2 onResume MainActivity onStop
退出
MainActivity2
,生命周期如下MainActivity2 onPause MainActivity onRestart MainActivity onStart MainActivity onResume MainActivity2 onStop MainActivity2 onDestroy
旋转屏幕,生命周期如下
MainActivity onPause MainActivity onStop MainActivity onDestroy MainActivity onCreate MainActivity onStart MainActivity onResume
-
新增一个按钮,实现点击出现弹窗
// 显示弹窗 binding.btnDialog.setOnClickListener { // 创建并显示一个 AlertDialog AlertDialog.Builder(this).apply { setTitle("提示") // 设置对话框标题 setMessage("确定要执行此操作吗?") // 设置对话框内容 setCancelable(false) // 点击外部是否可取消 // “确认”按钮 setPositiveButton("确定") { dialog, _ -> // 在这里处理“确定”点击事件 Toast.makeText(this@MainActivity, "已确认", Toast.LENGTH_SHORT).show() dialog.dismiss() } // “取消”按钮 setNegativeButton("取消") { dialog, _ -> // 在这里处理“取消”点击事件 dialog.dismiss() } // (可选)中性按钮 setNeutralButton("稍后再说") { dialog, _ -> // 在这里处理中性按钮点击 dialog.dismiss() } }.show() }
我们显示弹窗或取消弹窗,我们可以发现,对话框(
AlertDialog
)的show()
和dismiss()
操作本质上只是向系统的窗口管理器(WindowManager)添加或移除一个对话框窗口,不会改变宿主Activity
的可见性或焦点,因此不会触发该Activity
的任何生命周期回调(如onPause()
、onStop()
或onResume()
),PopupWindow
也是同理。
7. 常见 Activity
面试题
-
解释 Android Activity 的完整生命周期,并说明各个回调的主要作用
Android Activity 的生命周期由七个核心回调方法组成:
-
onCreate()
:首次创建 Activity 时调用,用于初始化 UI、绑定数据和恢复持久状态。 -
onStart()
:Activity 即将对用户可见时调用,可在此注册广播接收器或启动不需要前台交互的服务 。 -
onResume()
:Activity 进入前台并获得焦点时调用,此后用户可与之交互,可在此恢复动画或摄像头预览等 。 -
onPause()
:当系统准备启动新 Activity 或 Activity 部分可见时调用,应尽快保存易丢失的数据并停止动画。 -
onStop()
:Activity 完全不可见时调用,可释放重量级资源(如相机、传感器监听)。 -
onRestart()
:从停止状态恢复到可见之前调用,用于重新注册监听或恢复短期资源。 -
onDestroy()
:Activity 被销毁前调用,用于彻底释放资源并保存最终状态。
-
-
描述
onSaveInstanceState()
与onRestoreInstanceState()
的区别,以及它们分别何时被调用onSaveInstanceState(Bundle outState)
:在 Activity 即将被系统销毁以释放资源(如屏幕旋转、后台回收)前调用。开发者应在此将用户输入、滚动位置等易丢失的 UI 状态存入outState
,并必须调用super.onSaveInstanceState(outState)
以便系统自动保存视图层级的状态。onRestoreInstanceState(Bundle savedInstanceState)
:在onStart()
之后、onResume()
之前自动被调用,且只有当savedInstanceState
非空时才触发。开发者在此可以从savedInstanceState
中恢复先前保存的状态,也可选择在onCreate(Bundle)
中统一恢复。
调用时机对比:
-
保存时机:
onSaveInstanceState()
在每次 暂停(onPause()
)之后、停止(onStop()
)之前执行。 -
恢复时机:系统在新实例
onCreate()
完成后,紧接着调用onRestoreInstanceState()
,然后才是onResume()
。
-
屏幕旋转时,Activity 的生命周期会如何变化?如何正确保存并恢复数据?
默认情况下,屏幕旋转属于一次 配置变化,系统会销毁当前 Activity 并重建新实例,从而加载与当前方向匹配的资源。完整的生命周期回调顺序为:
onPause() → onSaveInstanceState() → onStop() → onDestroy() 新实例创建 → onCreate(Bundle) → onStart() → onRestoreInstanceState(Bundle) → onResume()
-
在
onSaveInstanceState()
中保存必要的 UI 状态(例如表单内容、滚动位置)。 -
在
onCreate(Bundle)
或onRestoreInstanceState(Bundle)
中读取并恢复这些状态。 -
若想避免重建,可在 Manifest 中为 Activity 添加
android:configChanges="orientation|screenSize"
,并重写onConfigurationChanged()
手动处理布局更新。
-
-
解释 Activity 的三种启动模式:
singleTop
、singleTask
和singleInstance
,以及它们对生命周期的影响standard
(默认模式):每次调用startActivity()
都会创建新的实例。singleTop
:如果目标 Activity 已位于任务栈顶部,则不创建新实例,而是通过onNewIntent()
复用现有实例,并触发onNewIntent()
回调;否则按standard
方式创建singleTask
:系统会检查任务栈中是否存在目标 Activity 实例:- 若存在,则将该实例之上的所有 Activity 弹出(调用它们的
onPause()
、onStop()
、onDestroy()
),并通过onNewIntent()
传递新 Intent; - 若不存在,则创建新实例。 这种模式确保一个任务栈只保存一个该 Activity 实例
- 若存在,则将该实例之上的所有 Activity 弹出(调用它们的
singleInstance
:类似于singleTask
,但该 Activity 会成为独立任务,其他所有 Activity 都会在其他任务中启动,保证该实例是该任务栈中唯一 Activity。
这些启动模式会影响 是否创建新实例 以及 哪些生命周期方法(如
onCreate()
、onNewIntent()
)被调用,是面试中的高频考点 -
描述
AsyncTask
与Activity
生命周期的关系,以及可能带来的问题和解决方案-
生命周期脱节:
AsyncTask
并不绑定宿主 Activity 的生命周期。如果在 Activity 中启动异步任务,而用户发生配置变化(如旋转)导致 Activity 重建,旧的AsyncTask
仍会在后台运行并在完成后调用过时的 Activity 引用,可能引发内存泄漏或NullPointerException
。 -
常见问题:
- Context 泄漏:
AsyncTask
持有对旧 Activity 的引用。 - 重复执行或回调丢失:重建后的 Activity 无法接收旧任务的结果。
- Context 泄漏:
-
解决方案:
-
使用 静态内部类 或 弱引用 来持有 Context,避免泄漏;
-
使用 Fragment + setRetainInstance(true) 或 ViewModel + LiveData(Jetpack 架构组件)来管理异步任务,使其与配置变化无缝协作,无需手动取消或重绑定任务
-
-