2.1 实验:Activity 生命周期
1.1 本次实验的项目参数(供参考)
本次已提供初始工程,无需手动新建项目。以下参数是该项目的基本信息,导入后可在 Android Studio 中核对:
| 参数名 | 值 |
|---|---|
APP_NAME | LifecycleDemo |
PACKAGE_NAME | cn.edu.sziit.android.lifecycledemo |
MIN_SDK | API 24 |
1.2 实验提交说明
本实验共需提交 至少 6 张截图(步骤 3 代码截图可按代码段分多张)。每张截图的具体要求会在对应步骤末尾以醒目提示框标注,请操作完当步后立即截图保存。
下方的作业表列出了所有提交要求,截图可直接粘贴到 Word 文档中提交:
📄 下载本次实验作业表(Word 版)
所有截图需包含你的学号和姓名(步骤 3.3 会说明如何添加)。
1.3 导入项目
本次实验已为你准备好初始项目,无需从零新建。请按以下步骤操作:
步骤 1:点击下方链接下载项目压缩包:
📦 下载 LifecycleDemo.zip步骤 2:将压缩包解压到合适目录。
⚠️ 解压路径要求:
- 不能含有中文字符
- 不能含有空格
- 建议放在非 C 盘的专用目录,例如:
D:\AndroidProjects\LifecycleDemo
步骤 3:将解压后的项目导入 Android Studio,并完成 Gradle Sync。
参考《导入已有 Android 项目》完成导入与同步。
完成后确认项目可以正常运行到模拟器,如下所示。
截图模拟器中 LifecycleDemo 的初始运行界面,确认项目已正确导入并可以运行。截图需满足:
- 可以看到模拟器设备画面(含状态栏/导航栏)
- App 界面显示「Activity 生命周期实验」文字
什么是 Activity 生命周期?
Activity 在创建、使用、退出的整个过程中,会依次经历一系列状态变化。Android 系统通过调用特定方法来通知 Activity 当前所处的状态,这些方法合称为生命周期方法。
合理利用生命周期方法,可以让应用在以下场景中表现正确:按 Home 键后暂停播放、切回后恢复播放、旋转屏幕后保留用户输入等。
七个核心生命周期方法:
| 方法名 | 触发时机 | 说明 |
|---|---|---|
onCreate | Activity 首次创建 | 初始化视图,必须在此调用 setContentView |
onStart | Activity 变为可见 | 界面开始呈现给用户 |
onResume | Activity 获得焦点,进入前台 | 用户可以开始与界面交互 |
onPause | Activity 失去焦点 | 另一个 Activity 正在进入前台;不要在此做耗时操作 |
onStop | Activity 完全不可见 | 停止不必在后台运行的操作 |
onRestart | Activity 从停止状态恢复 | 在 onStop 之后、再次 onStart 之前触发 |
onDestroy | Activity 即将被销毁 | 释放所有资源 |
3.1 重写 MainActivity 的生命周期方法
打开 MainActivity.kt,按照以下代码示例进行修改:
package cn.edu.sziit.android.lifecycledemo
import android.os.Bundleimport android.util.Logimport androidx.appcompat.app.AppCompatActivityimport cn.edu.sziit.android.lifecycledemo.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
companion object { private const val TAG = "LifecycleDemo" }
override fun onStart() { super.onStart() Log.d(TAG, "onStart") }
override fun onResume() { super.onResume() Log.d(TAG, "onResume") }
override fun onPause() { super.onPause() Log.d(TAG, "onPause") }
override fun onStop() { super.onStop() Log.d(TAG, "onStop") }
override fun onRestart() { super.onRestart() Log.d(TAG, "onRestart") }
override fun onDestroy() { super.onDestroy() Log.d(TAG, "onDestroy") }}3.2 在日志中添加你的身份信息
为了让后续截图能够标明提交者身份,请在 onCreate 内 Log.d(TAG, "onCreate") 的下一行,追加以下代码(将引号内容替换为你自己的学号和姓名):
Log.d(TAG, "学号: 20230001 姓名: 张三") // 替换为你的学号和姓名截图 MainActivity.kt 中所有 Log.d 相关代码。由于代码较长,可以分多张截图,每张截图只需覆盖含 Log.d 语句的代码区域即可。要求:
- 每张截图只需覆盖含
Log.d语句的区域,无需在同一张图中包含全部方法 - 至少一张截图须含
Log.d(TAG, "学号: ... 姓名: ...")那一行,证明已添加身份识别日志
4.1 打开 Logcat 面板
在底部工具栏点击 Logcat,或通过菜单View → Tool Windows → Logcat:
4.2 设置过滤条件
在 Logcat 面板顶部的搜索栏中输入 package:mine tag=:LifecycleDemo:
过滤规则说明:
package:mine:只显示当前设备上正在调试的应用(即本项目)产生的日志,排除其他后台应用的干扰tag=:LifecycleDemo:精确匹配日志标签(也就是Log.d()函数调用时传入的第一个参数的值)等于LifecycleDemo的日志行(=:表示完全相等匹配,区别于:的包含匹配)
两个条件共同作用,表示”只看本应用里、TAG 精确为 LifecycleDemo 的日志”,从而快速过滤掉系统及其他模块产生的噪声日志。
4.3 运行 App 并观察日志
点击 ▶ Run ‘app’ 运行。App 启动后,Logcat 中应出现:
D LifecycleDemo onCreateD LifecycleDemo onStartD LifecycleDemo onResume这三行日志对应 Activity 从创建到可交互的完整流程。
运行 App 后,截图 Logcat 窗口。截图需同时满足以下条件:
- 日志区域包含
onCreate、onStart、onResume三行日志 - 日志区域包含你在步骤 3.3 中添加的学号姓名日志行
在 App 运行状态下,依次进行以下操作,观察 Logcat 中出现的方法序列,并与预期对照:
| 操作 | 预期触发的方法序列 |
|---|---|
| 启动 App | onCreate → onStart → onResume |
| 按 Home 键 | onPause → onStop |
| 从最近任务切回 App | onRestart → onStart → onResume |
| 旋转屏幕 | onPause → onStop → onDestroy → onCreate → onStart → onResume |
| 按返回键退出(Android 12 以下) | onPause → onStop → onDestroy |
| 按返回键退出(Android 12 及以上,根 Activity) | onPause → onStop(不触发 onDestroy) |
注意:旋转屏幕时,Android 会完整销毁并重建 Activity。这意味着你在代码中定义的所有变量(如计数器、输入框内容)都会被重置。
在模拟器中,使用工具栏的旋转按钮来旋转屏幕:
模拟器侧边工具栏 → 旋转(↺ / ↻)在模拟器中旋转屏幕,截图 Logcat 窗口。截图需同时满足以下条件:
- 可见完整的销毁重建序列:
onPause→onStop→onDestroy→onCreate→onStart→onResume(6 个方法全部出现) - 日志区域包含你的学号姓名日志行
注意(Android 12 及以上):从 Android 12(API 31)开始,系统对根 Activity(Root Activity,即应用的入口 Activity) 的返回键行为进行了修改:按下返回键不再销毁 Activity,而是将整个应用移到后台,等同于按 Home 键的效果。因此你会观察到
onPause→onStop,但不会触发onDestroy。原因:系统为了改善用户体验,避免用户误操作按返回键导致应用退出、下次重启时需要重新加载。如果你需要在模拟器上实际看到
onDestroy被根 Activity 触发,可以通过最近任务列表将应用划掉(即 Force Stop),或调用代码中的finish()。
onPause 和 onStop 是两个容易混淆的生命周期方法:
onPause:Activity 失去焦点时触发,此时 Activity 仍然可见onStop:Activity 完全不可见时才触发
当系统权限请求对话框弹出时,Android 实际上启动了一个独立的系统 Activity(PermissionController),当前 Activity 仍然在背后可见,因此只触发 onPause,不触发 onStop。
注意:
AlertDialog.Builder(...).show()弹出的对话框不是独立的 Activity,而是绘制在当前 Activity 同一个Window中的浮层,不会触发任何生命周期回调。
6.1 在 AndroidManifest.xml 中声明相机权限
打开 app/src/main/AndroidManifest.xml,在 <manifest> 标签内添加相机权限声明:
<manifest ...> <!-- 添加相机权限声明 --> <uses-feature android:name="android.hardware.camera" android:required="false" /> <uses-permission android:name="android.permission.CAMERA" /> ...</manifest>6.2 在布局中添加”申请权限”按钮
打开 app/src/main/res/layout/activity_main.xml,在 <TextView> 之后追加按钮:
<?xml version="1.0" encoding="utf-8"?><LinearLayout ... >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Activity 生命周期实验" android:textSize="20sp" />
<!-- 在UI上新增按钮 --> <Button android:id="@+id/btnRequestPermission" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="申请相机权限" />
</LinearLayout>6.3 在 MainActivity.kt 中实现权限申请
在 class 内、onCreate 之前注册权限申请 Launcher,再在 onCreate 中绑定按钮点击事件:
package cn.edu.sziit.android.lifecycledemo
import android.Manifestimport android.os.Bundleimport android.util.Logimport androidx.activity.result.contract.ActivityResultContractsimport androidx.appcompat.app.AppCompatActivityimport cn.edu.sziit.android.lifecycledemo.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
...
// 注册权限申请 Launcher,必须在 onCreate 之前完成 private val requestCameraPermission = registerForActivityResult( ActivityResultContracts.RequestPermission() ) { isGranted -> Log.d(TAG, "权限结果:${if (isGranted) "已允许" else "已拒绝"}") }
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ...
// 在 onCreate 函数的末尾,点击按钮,触发系统权限对话框 binding.btnRequestPermission.setOnClickListener { Log.d(TAG, "请求 CAMERA 权限") requestCameraPermission.launch(Manifest.permission.CAMERA) } } ...}6.4 验证 Logcat 输出
运行 App,点击”申请相机权限”按钮,系统权限对话框弹出,当前 Activity 在背后仍然可见:
| 操作 | 预期出现 | 预期不出现 |
|---|---|---|
| 权限对话框弹出 | onPause | onStop、onDestroy |
| 点击”允许”或”拒绝”后 | onResume | — |
与按 Home 键(触发 onPause → onStop)对比,可以明确看出:系统弹出独立 Activity 形式的对话框时,当前 Activity 只触发 onPause,不触发 onStop。
本截图任务需提交 2 张截图,对比说明权限对话框只触发 onPause 而不触发 onStop 的现象。
截图 A — 权限对话框弹出时,截图 Logcat 窗口,需满足:
- 日志中可以看到
onPause(已触发) - 日志中没有
onStop或onDestroy(未触发) - 包含你的学号姓名日志行
截图 B — 点击允许 / 拒绝后,截图 Logcat 窗口,需满足:
- 日志中可以看到
onResume(Activity 重新获焦) - 包含你的学号姓名日志行
7.1 Logcat 过滤不到日志
检查 Logcat 搜索栏是否输入了 package:mine tag=:LifecycleDemo(注意大小写与代码中的 TAG 常量一致,且使用 =: 精确匹配)。如果仍看不到,确认 App 实际运行的是最新代码(重新 Run)。