
写 Android 代码的第三年,我依然会在 onCreate 里犯蠢。
听起来不可思议,但这事真发生过——我曾经在 Activity 的 onCreate 里 new 了一个 Handler,然后用 postDelayed 做了一个延时任务,期望页面销毁时这个任务能"自动消失"。结果用户从 A 页面跳到 B 页面再回来时,屏幕上跳出了一个莫名其妙的 Toast。
那一刻我深刻意识到:生命周期这门课,永远值得重学。
今天这篇文章是这个 100 篇系列的开篇,咱们就从最基础的 Activity 生命周期开始。但请放心,我不会再念一遍那张"教科书配图"——onCreate → onStart → onResume 这种倒背如流的流程。今天我想跟你聊 5 个真实工程里我见过、踩过、修过的坑。
一、先用一张图,搞清楚"用户视角"的真实流转
教科书里的生命周期图过于抽象。我们换一种画法,从用户真实操作的角度看:
记住这张表,比记住任何源码解读都有用——因为它是从真实事故里提炼出来的。
二、坑 1:在 onCreate 里做"重型操作"
事故现场
某个首页 Activity,onCreate 里同步读取了 SharedPreferences 里的一份 1.2MB 配置 JSON。冷启动时发现,从桌面图标点击到首屏可交互,足足卡了 800ms。
原因
onCreate 是主线程同步执行的。在 onCreate 里做任何 IO、文件解析、Bitmap 解码,都会直接拖慢首帧。
正确姿势
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 启动后再异步加载
lifecycleScope.launch {
val config = withContext(Dispatchers.IO) {
loadConfigFromDisk()
}
applyConfig(config)
}
}
这里用到 lifecycleScope,它会在 Activity 销毁时自动取消所有启动的协程——这就是我们接下来第 3 篇要讲的协程的妙处。
一个判断标准
onCreate 里只做"渲染 UI 必须做的事"。任何可以异步、可以延后、可以缓存的事,都不要放在 onCreate。
三、坑 2:横竖屏切换导致用户输入丢失
事故现场
一个填写表单的页面,用户填了 5 个字段,切了一次横屏,结果全没了。QA 复现了 5 次,每次都复现。
原因
默认情况下,配置变更(Configuration Change)会重建 Activity。EditText 的文本会自动保存,但业务数据不会——比如你把表单数据放在一个 Activity 字段里,重建之后那个字段是空的。
三种解决方案
方案 A:自己处理 onSaveInstanceState
class FormActivity : AppCompatActivity() {
private var formDraft: FormDraft? = null
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
formDraft?.let { outState.putParcelable("draft", it) }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
formDraft = savedInstanceState?.getParcelable("draft")
?: FormDraft()
}
}
方案 B:用 ViewModel(推荐)
class FormViewModel : ViewModel() {
val formState = MutableStateFlow(FormDraft())
}
class FormActivity : AppCompatActivity() {
private val viewModel: FormViewModel by viewModels()
// 配置变更时,ViewModel 不会被重建,formState 自动保留
}
方案 C:禁止重建(不推荐,除非有特殊理由) 在 manifest 里加 android:configChanges="orientation|screenSize"。 代价:旋转后你得自己重新设置 View,且不能用 onConfigurationChanged 干重活——会卡顿。
个人建议:方案 B。配置变更本来就是 Android 的设计,不要绕过它。
四、坑 3:把"初始化 SDK"放在 onResume
事故现场
某 IM SDK 需要在用户进入聊天页时建立长连接。原本写在 onResume 里,结果发现用户从聊天列表快速滑过聊天页(点开又立即返回),SDK 的 connect/disconnect 来回打了好几次。
原因
onResume 每次可见都触发,包括从其他页面返回时。如果把"重型副作用"放在 onResume,行为会被用户操作频繁触发。
正确姿势:用 lifecycle-aware 组件
class ChatActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
// 轻量级:UI 状态刷新
refreshUnreadBadge()
}
})
// 重型副作用放在 STARTED 而非 RESUMED,可以用协程:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
imClient.connect()
}
}
}
}
repeatOnLifecycle(STARTED) 会在 STARTED 时启动、在 STOPPED 时取消——比 onStart/onStop 手动配对优雅得多。
五、坑 4:忽略后台时的"onStop 才做事"原则
事故现场
一个视频 App 把播放器释放逻辑放在了 onPause。结果从评论页切回视频页时,画面已经先冻住了 200ms,体验很差。
原因
onPause 触发时机非常早——另一层半透明 Activity 弹起来时也会触发。真要做"释放",得在 onStop 里做。
区分原则
口诀:
onPause 管"还看得见",onStop 管"完全看不见"。
六、坑 5:相信"用户按返回时一定会走 onDestroy"
事故现场
某次崩溃统计里看到一批 IllegalStateException,全是从我们自己写的"清理代码"里抛出来的——我们假设 Activity 销毁时一定会走 onDestroy,然后清理里访问了一个早就被回收的对象。
真相
Activity 不一定走 onDestroy。比如:
用户按 Home,进程被杀后再回来——onDestroy 根本不会走
系统内存紧张时直接回收进程——onDestroy 也不会走
用户只是切换了其他 Activity——onStop 之后进程被杀
正确姿势:把"必须做的清理"放进 ViewModel.onCleared 或 use case 的 finalize 里
class HeavyWorkViewModel : ViewModel() {
private val job = SupervisorJob()
fun startHeavyTask() {
viewModelScope.launch(job) {
// ...
}
}
override fun onCleared() {
super.onCleared()
job.cancel() // ViewModel 销毁时一定会触发
}
}
ViewModel 的 onCleared 是 Activity finish() 时一定会触发的(除非整个进程被杀,但那种情况本来就不用清理)。
小结
Activity 生命周期看似是入门第一课,但90% 的 Android 工程师在工作 3 年后还会被它咬一口。今天聊的 5 个坑,我特意挑的是工程中最高频的:
onCreate 只做"渲染必需"——其他都异步。
配置变更用 ViewModel 兜底——别自己玩 Bundle。
重型副作用用
repeatOnLifecycle——别往 onResume 里塞。释放资源看 onStop——onPause 管太宽。
清理放 ViewModel.onCleared——别赌 onDestroy。
下篇预告
下一篇是《第 2 篇:为什么 Android 工程师都在转 Compose?》。 我们将从 View 与 XML 的老故事讲起,聊 Compose 为什么是真正的范式转移,而不是又一个 UI 框架。
参考阅读
Android 官方文档:Activity Lifecycle
"Jetpack ViewModel 与 SavedStateHandle 最佳实践"
《Android 编程权威指南》(第 4 版)第 3 章