第二个Activity

第二个Activity:保留第四版正文机制,以所有者—状态—结果合同、事件轨迹和章专属故障完成可重放验收。

学习目标

  • 能沿“第二个Activity”的用户事件解释Android组件、状态所有者、线程与销毁边界。
  • 能围绕“学会启动第二个 Activity——显式 Intent、Activity 间传递数据、Activity Result API 接收返回结果。”改出一个可运行结果,并用前后状态而非组件数量验收。
  • 能在旋转、进程重建、拒权、离线或无效输入中选择适用反例,定位首个状态分叉。
  • 能用Intent合同、解析目标、权限、返回结果和重复副作用独立重放结论,并标明第四版机制与现代targetSdk政策的边界。

一个 App 里通常不止一个屏幕

列表页点进详情、设置页返回主界面——每个屏幕通常是一个 。用 告诉系统「打开哪个 Activity」。

启动第二个 Activity

// CrimeListActivity.kt
val intent = Intent(this, CrimeDetailActivity::class.java)
startActivity(intent)

传递数据

intent.putExtra("crime_id", crime.id)
intent.putExtra("crime_title", crime.title)
 
// CrimeDetailActivity.kt
val crimeId = intent.getStringExtra("crime_id")

复杂对象用 Parcelable(Kotlin @Parcelize)或 Serializable,推荐 Parcelable。

Activity Result API

private val detailLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val updatedId = result.data?.getStringExtra("updated_id")
        // 刷新列表
    }
}
 
fun openDetail(crimeId: String) {
    val intent = Intent(this, CrimeDetailActivity::class.java)
    intent.putExtra("crime_id", crimeId)
    detailLauncher.launch(intent)
}
 
// 详情页返回
setResult(Activity.RESULT_OK, Intent().putExtra("updated_id", crime.id))
finish()
分步1 / 3

① 列表启动详情

列表页调用 detailLauncher.launch(intent) 启动详情 Activity。intent 中携带 crime_id 等必要数据,详情页在 onCreate 中通过 getStringExtra 读取。

val intent = Intent(this, CrimeDetailActivity::class.java)
intent.putExtra("crime_id", crime.id)
detailLauncher.launch(intent)

返回栈

为什么 startActivity 启动新 Activity 后按返回键能回到上一个?因为系统把同任务里的 Activity 放进一个(back stack,后进先出):启动新 Activity = 压栈、它成为栈顶并显示在屏幕上;按返回键 = 栈顶出栈销毁,下面那个浮上来重新显示。栈顶永远是用户当前看到的那一屏——这也是上面那条「忘记 finish()」陷阱的根源:没 finish,详情页就一直赖在栈顶。

可交互
▲ 栈底(先入)栈顶 = 当前可见 Activity ▾CrimeListActivity列表页(App 首屏)CrimeDetailActivity详情页

第 1 / 5 步 · ① App 启动:CrimeListActivity 入栈,成为栈顶(也是栈底),当前可见

点击播放,看一次「点进详情、再按返回」如何在返回栈里压栈又出栈;栈顶始终是当前屏幕上的那个 Activity。可暂停、单步、拖进度逐帧观察。

Activity 返回栈(后进先出):启动新 Activity = 压栈成为栈顶并显示,旧的下沉保活; 按返回键 = 栈顶出栈销毁,下一个浮上来重新显示。栈顶永远是用户当前看到的那一屏。

小结

  • 显式 Intent + startActivity / Result API
  • putExtra / getXxxExtra 传简单数据
  • setResult + finish 回传;检查 resultCode

练习

问题 1:“第6章 Your Second Activity”覆盖哪些正式节点和项目主线?

问题 2:怎样建立本页最小可执行实验?

问题 3:为什么只在正常点击路径运行不能证明完成?

问题 4:怎样设计能推翻当前实现的反例?

问题 5:从第4版语境迁移到现代目标SDK时如何控制变量?

问题 6:本页达到独立交接标准需要什么?

名词解释

本章出现的专业名词,用大白话再讲一遍。

Activity

Android 中承载单一界面的组件,通过 Intent 启动,在返回栈中按后进先出管理。

显式 Intent

通过 setClass/setComponent 指定目标 Activity 的 Intent,用于应用内导航。

Activity Result API

registerForActivityResult + ActivityResultContract,类型安全地启动 Activity 并接收结果。

返回栈

后进先出的 Activity 栈。按 Back 弹出当前 Activity,显示前一个。

“第二个Activity”不使用未获授权的纸书正文;InformIT出版信息授权电子版完整目录只用于确认第四版32章、269个正式目录节点和时代语境,第四版官方勘误用于识别工具链变更。下列中文解释、图示、交互、代码与练习均为独立教学重写,平台行为再以Android Developers的一手文档复核。

为什么“第二个Activity”必须回到可观察状态

“第二个Activity”的学习结果不是记住类名,而是能预测“学会启动第二个 Activity——显式 Intent、Activity 间传递数据、Activity Result API 接收返回结果。”在一次输入、一次重建和一次失败中的不同状态,并指出哪条Android合同产生差异。

第四版机制逐项深读

6. Your Second Activity

在“第二个Activity”中,“6. Your Second Activity”通过显式Intent启动目标并以extras/result交换最小数据;发送方、接收方和进程重建都必须验证缺失、类型错误和重复返回。

Setting Up a Second Activity

在“第二个Activity”中,“Setting Up a Second Activity”通过显式Intent启动目标并以extras/result交换最小数据;发送方、接收方和进程重建都必须验证缺失、类型错误和重复返回。

Starting an Activity

在“第二个Activity”中,“Starting an Activity”通过显式Intent启动目标并以extras/result交换最小数据;发送方、接收方和进程重建都必须验证缺失、类型错误和重复返回。

Passing Data Between Activities

在“第二个Activity”中,“Passing Data Between Activities”通过显式Intent启动目标并以extras/result交换最小数据;发送方、接收方和进程重建都必须验证缺失、类型错误和重复返回。

How Android Sees Your Activities

在“第二个Activity”中,“How Android Sees Your Activities”若依赖第四版时期API,应分开说明原书机制与现代平台政策,并用Android官方文档核对迁移边界。

Challenge: Closing Loopholes for Cheaters

在“第二个Activity”中,“Challenge: Closing Loopholes for Cheaters”用于推翻“学会启动第二个 Activity——显式 Intent、Activity 间传递数据、Activity Result API 接收返回结果。”的顺利路径:先写预期,再引入一个边界输入、重建或平台差异,并用状态、日志与用户结果解释首个分叉。

Challenge: Tracking Cheat Status by Question

在“第二个Activity”中,“Challenge: Tracking Cheat Status by Question”用于推翻“学会启动第二个 Activity——显式 Intent、Activity 间传递数据、Activity Result API 接收返回结果。”的顺利路径:先写预期,再引入一个边界输入、重建或平台差异,并用状态、日志与用户结果解释首个分叉。

“第二个Activity”验收回顾

“第二个Activity”只有在Intent合同、解析目标、权限、返回结果和重复副作用能够从相同基线再次得到相同断言时才通过;第四版机制与现代平台政策分别记录,不用新API名称掩盖旧行为。

← 上一页:Android应用的调试 · 下一页:SDK版本与兼容性 →

章专属可重放状态实验

先预测“启动第二界面、旋转、返回与重复结果到达”发生后,发送 Activity、目标 Activity 与 ActivityResult 合同应怎样改变题目答案、作弊决定、返回结果和已处理标记;再操作三个实验。第四版示例与当前 Android 政策分别记录,实验不把新 API 名称倒填为原书内容。

实验一:所有者—状态—结果合同

选择任一正式目录节点和正常/边界场景,检查它是否真的进入本章状态合同。目录标题只有同时出现在解释、可视状态和交付证据中才算覆盖。

Owner · state · observable result

第二个Activity:状态合同

以显式 Intent 启动 CheatActivity,并用最小 extras 与结果合同交换数据

验证场景

第四版正式目录节点

second-activity · 正常任务

6. Your Second Activity固定 SDK、设备配置和初始状态,触发“启动第二界面、旋转、返回与重复结果到达”

状态所有者发送 Activity、目标 Activity 与 ActivityResult 合同
受控状态题目答案、作弊决定、返回结果和已处理标记
触发事件启动第二界面、旋转、返回与重复结果到达

冻结入口:6. Your Second Activity

记录发送 Activity、目标 Activity 与 ActivityResult 合同的初始题目答案、作弊决定、返回结果和已处理标记

观察:Intent extras、实例 ID、结果码、消费标记和题目状态中的“6. Your Second Activity”轨迹

预期:由发送 Activity、目标 Activity 与 ActivityResult 合同提交题目答案、作弊决定、返回结果和已处理标记,并持续满足“缺失或重复结果不能让 GeoQuiz 错记作弊状态”

实验二:事件与生命周期轨迹

沿五次转换逐步执行“启动第二界面、旋转、返回与重复结果到达”。每一步只允许发送 Activity、目标 Activity 与 ActivityResult 合同按职责提交状态,并持续核对“缺失或重复结果不能让 GeoQuiz 错记作弊状态”。

Deterministic event replay

第二个Activity:事件轨迹

选择一次状态转换1 / 5

不变量:缺失或重复结果不能让 GeoQuiz 错记作弊状态

交付证据:Intent extras、实例 ID、结果码、消费标记和题目状态

实验三:章专属反例与同输入恢复

注入“目标 Activity 重建后再次返回同一结果,发送方重复提交”,保存第一个偏离点;撤销后以完全相同的 SDK、设备状态和用户事件重放。只有Intent extras、实例 ID、结果码、消费标记和题目状态一起恢复才算修复。

Fault · cancel · restore

第二个Activity:反例与恢复

故障:目标 Activity 重建后再次返回同一结果,发送方重复提交

1. 冻结输入一致

第 1 次使用相同 SDK、设备配置、初始状态与用户事件

2. 注入边界一致

保持正常输入不变,仅注入“目标 Activity 重建后再次返回同一结果,发送方重复提交”

3. 检查所有者一致

缺失或重复结果不能让 GeoQuiz 错记作弊状态

4. 核对结果一致

Intent extras、实例 ID、结果码、消费标记和题目状态

讨论

评论区加载中…