3. Activity生命周期:从生到死的完整旅程
说实话,Activity生命周期这个概念,是Android开发里最基础也最重要的知识点之一。我当年刚入行时,就因为没搞懂生命周期,写了个App一旋转屏幕就崩溃——那场面,真是记忆犹新。
今天咱们就来聊聊,一个Activity从创建到销毁,到底经历了什么。
3.1 Activity的四种状态
一个Activity,说白了就是手机屏幕上的一扇窗户。它可能完全展示在你面前,也可能被别的窗户挡住,甚至被系统直接关掉。这四种状态,就是它不同时刻的“生存状态”。
| 状态 | 含义 | 用户能看到吗? | 能交互吗? |
|---|---|---|---|
| 运行状态 | Activity在前台,完全可见 | ✅ 能 | ✅ 能 |
| 暂停状态 | 被另一个Activity部分遮挡(比如弹窗) | ✅ 能(部分) | ❌ 不能 |
| 停止状态 | 完全被另一个Activity覆盖 | ❌ 不能 | ❌ 不能 |
| 销毁状态 | 被系统回收或主动finish() | ❌ 不能 | ❌ 不能 |
嗯,这里要注意:暂停和停止的区别,关键看“是否完全被挡住”。我见过不少新手把这两个搞混,结果在onPause里做了本该在onStop做的事。
3.2 六个核心回调方法
Android系统通过六个回调方法,来通知Activity“你的状态变了”。这六个方法,就像Activity一生的六个重要节点。
生命周期回调顺序(标准情况):
onCreate() → onStart() → onResume() → (运行中) → onPause() → onStop() → onDestroy()
3.2.1 onCreate() — 出生
这是Activity的第一个方法。只调用一次。我习惯在这里做三件事:
- 调用setContentView()加载布局
- 初始化控件(findViewById)
- 恢复之前保存的状态(如果有的话)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化控件
TextView tvHello = findViewById(R.id.tv_hello);
// 恢复状态
if (savedInstanceState != null) {
String savedText = savedInstanceState.getString("key_text");
tvHello.setText(savedText);
}
}
3.2.2 onStart() — 即将亮相
Activity变得“可见”了,但还不能交互。这个阶段很短。我个人很少在这里写复杂逻辑,顶多做一些动画的初始化。
3.2.3 onResume() — 正式登场
用户终于可以操作了!这是Activity最活跃的时刻。我在项目中遇到过一个问题:用户从别的App切回来,发现界面卡住了。后来发现是onResume里做了耗时操作。记住:onResume里不要做耗时操作。
3.2.4 onPause() — 准备退场
Activity即将失去焦点。你想想看,这时候用户可能按了Home键,或者来了个电话。你需要在这里:
- 保存一些临时数据(比如表单填写的内容)
- 释放摄像头、传感器等独占资源
- 停止动画
⚠️ 注意:onPause()的执行时间非常短(Android官方建议在几百毫秒内完成)。不要在这里做数据库写入、网络请求等耗时操作。我曾经见过一个App,在onPause里写数据库,结果导致新Activity启动卡顿——用户体验极差。
3.2.5 onStop() — 完全隐藏
Activity完全不可见了。这时候你可以做一些“不那么紧急”的清理工作,比如:
- 停止后台线程
- 释放大内存对象(比如Bitmap)
- 保存持久化数据
3.2.6 onDestroy() — 终结
Activity即将被销毁。这是最后的机会,释放所有还没释放的资源。但说实话,我很少在onDestroy里做复杂操作——因为系统可能根本不调用它(比如用户强制杀进程)。
3.3 生命周期实战演示
光说不练假把式。咱们写个简单的Demo,看看每个回调什么时候被调用。
public class LifecycleActivity extends AppCompatActivity {
private static final String TAG = "LifecycleDemo";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lifecycle);
Log.d(TAG, "onCreate 被调用");
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart 被调用");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume 被调用");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause 被调用");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop 被调用");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy 被调用");
}
}
运行这个App,打开Logcat,你会看到这样的输出:
D/LifecycleDemo: onCreate 被调用
D/LifecycleDemo: onStart 被调用
D/LifecycleDemo: onResume 被调用
然后按Home键:
D/LifecycleDemo: onPause 被调用
D/LifecycleDemo: onStop 被调用
再切回来:
D/LifecycleDemo: onRestart 被调用 // 注意这个!从停止状态恢复时会调用
D/LifecycleDemo: onStart 被调用
D/LifecycleDemo: onResume 被调用
最后按返回键:
D/LifecycleDemo: onPause 被调用
D/LifecycleDemo: onStop 被调用
D/LifecycleDemo: onDestroy 被调用
💡 我的小技巧:开发时,我习惯在每个回调方法里都加Log。这样能直观地看到Activity的“人生轨迹”。调试生命周期相关bug时,这招特别管用。
3.4 常见场景的生命周期变化
| 操作 | 回调顺序 |
|---|---|
| 首次打开App | onCreate → onStart → onResume |
| 按Home键 | onPause → onStop |
| 从最近任务切回 | onRestart → onStart → onResume |
| 按返回键 | onPause → onStop → onDestroy |
| 旋转屏幕 | onPause → onStop → onDestroy → onCreate → onStart → onResume |
| 来电 | onPause → onStop(如果电话全屏) |
说到旋转屏幕,我得多说两句。默认情况下,旋转屏幕会导致Activity销毁重建。这意味着你填了一半的表单、播放到一半的视频,全没了。解决办法?要么在AndroidManifest里配置android:configChanges="orientation",要么在onSaveInstanceState里保存数据。
核心要点总结:
- onCreate:只调用一次,做初始化
- onResume:用户能交互了,别做耗时操作
- onPause:快速保存临时数据,别拖太久
- onStop:可以做一些重量级的保存工作
- onDestroy:最后的清理机会,但别依赖它
好了,Activity的生命周期就聊到这儿。说白了,它就是一套“什么时候该干什么事”的规范。搞懂了它,你写出来的App才不会出现“切出去再切回来数据丢了”、“旋转屏幕崩溃了”这种低级问题。下一章咱们聊聊Intent和Activity之间的数据传递,到时候你就知道怎么在Activity之间优雅地传数据了。