Activity 不是应用,进程也不是承诺
屏幕旋转、退到后台、系统杀进程看起来都像「页面没了」,但它们要恢复的东西完全不同。
用户在消息框里打了半句,切到相机拍附件。系统因内存压力杀掉应用进程,稍后从最近任务恢复。哪样东西还一定活着?
remember里的草稿ViewModel里的草稿Application单例里的草稿- 没有一个;除非它进入可恢复状态或持久层
三个寿命,不是一条生命周期
组合寿命最短:某段 Composable 是否还在 Composition 中。Activity 寿命更长:从创建到销毁,中间经历前台、可见、停止。进程寿命最不可控:当进程不再重要,系统可以直接回收,不会先给你一个可靠的「最后机会」。
配置变更时,旧 Activity 销毁、新 Activity 创建,但默认情况下同一 ViewModelStore 能把 ViewModel 交给新 Activity。进程死亡时,内存里的 Activity、ViewModel、单例和协程一起消失。系统保存的只是重建入口与少量实例状态,不是你的对象图。
四类状态,四个去处
| 状态 | 例子 | 合适去处 |
|---|---|---|
| 纯视觉瞬时状态 | 展开项、按压、动画进度 | remember |
| 可序列化的 UI 输入 | 草稿、筛选项、滚动位置 | rememberSaveable 或 SavedStateHandle |
| 页面业务状态 | 会话详情、发送状态 | ViewModel 从数据层重建 |
| 不能丢的事实 | 待发消息、患者同意记录 | Room、文件或服务端 |
Bundle 不是数据库。保存太多对象既慢又可能超过 Binder 事务限制。正确策略通常是保存一个能重新定位数据的键,例如会话 ID、草稿 ID;真正的数据从持久层重新观察。
回调要理解成可见性,不是业务流程
onStart 到 onStop 大致对应可见,onResume 到 onPause 大致对应可交互。不要把「发消息必须完成」绑在某个回调上:多窗口、权限对话框、透明 Activity 都会让旧口诀失真。
Compose 收集 Flow 时,用 collectAsStateWithLifecycle() 让收集跟可见生命周期联动;必须跨进程可靠完成的上传交给 WorkManager。一个是停止浪费当前进程,另一个是即使当前进程消失也要重试。
给一个最小 ViewModel 加可恢复草稿。进程重建后,框架能从保存状态恢复 draft;消息列表则应该由 conversationId 重新查询:
可恢复:conversationId、draft 重新加载:messages 不可依赖:ViewModel 对象本身
class ChatViewModel(
private val saved: SavedStateHandle,
private val repository: ChatRepository
) : ViewModel() {
val conversationId: String = checkNotNull(saved["conversationId"])
val draft = saved.getStateFlow("draft", "")
val messages = repository.observeMessages(conversationId)
fun onDraftChanged(value: String) {
saved["draft"] = value
}
}在 Android Studio 新建一个 Compose Activity,把「开发者选项 → 不保留活动」打开,再配合 adb shell am kill your.package 观察两种重建。不要用「强行停止」代替进程死亡测试,它的语义不同。
用户从聊天页跳到系统相机或文件选择器时,你的页面可能停止甚至被回收。临床照片流程不能只把待上传字节握在 ViewModel 里;至少要有受控的临时文件 URI、可恢复的工作记录和明确的清理策略。
「放进 ViewModel 就不会丢。」ViewModel 只跨配置变更,不跨进程死亡;它也不是通用缓存。
先问要跨过哪种死亡:重组、导航离开、Activity 重建、进程死亡、设备重启。答案决定 remember、ViewModel、保存状态、数据库或 WorkManager。
答案是第 4 项。前三项都在进程内存里。需要恢复的短小 UI 输入进入保存状态;不能丢的业务事实必须先持久化。面试里主动区分配置变更与进程死亡,通常比背全套回调更能说明你真的理解 Android。
这一章的一句话
状态放在哪里,取决于它要活过哪一种死亡,而不是哪个 API 最顺手。
下一章把进程以外的系统门口补齐:一次上传究竟该用协程、WorkManager 还是前台服务?选错以后,差别不是优雅程度,而是任务会不会凭空消失。