3. Activity生命周期与启动模式
说实话,Activity是Android里最基础也最核心的概念。你写的每个界面,背后都是一个Activity在撑着。我记得刚入行那会儿,面试官最爱问的就是「说说Activity的生命周期」。当时觉得背下来就行,后来真做项目才发现——光背没用,得理解它为什么这么设计。
今天咱们就把这块彻底讲透。包括四种状态、七个回调方法、四种启动模式,还有Intent怎么跳转。嗯,内容不少,但都是干货。
3.1 Activity的四种状态
Activity在运行过程中,会在四种状态之间切换。说白了,就是系统根据你的操作和资源情况,给Activity安排不同的「活法」。
| 状态 | 说明 | 典型场景 |
|---|---|---|
| 运行状态 | Activity位于前台,用户可交互 | 你正在刷朋友圈 |
| 暂停状态 | Activity被部分遮挡,但仍可见 | 弹出一个对话框 |
| 停止状态 | Activity完全不可见 | 按Home键回到桌面 |
| 销毁状态 | Activity被系统回收或主动finish | 按返回键退出 |
你想想看,系统为什么要搞这四种状态?其实就是为了合理分配内存和CPU。运行状态的Activity优先级最高,系统轻易不会杀它。停止状态的Activity呢?如果内存不够,系统第一个拿它开刀。
3.2 七个回调方法详解
Activity的生命周期回调,说白了就是系统给你发的「通知」。每个通知告诉你:你的Activity现在处于什么阶段,你可以趁机做点啥。
我个人习惯把七个回调分成三组来记:
3.2.1 创建与销毁组
- onCreate():Activity第一次创建时调用。做初始化工作——加载布局、绑定控件、初始化数据。只调用一次。
- onDestroy():Activity被销毁前调用。释放资源、取消注册、停止线程。也是只调用一次。
这里有个坑:onCreate里不要做耗时操作。我见过有人直接在onCreate里请求网络,结果界面卡了好几秒才出来。用户体验极差。
3.2.2 可见与不可见组
- onStart():Activity变得可见,但还不能交互。可以在这里做一些准备动画或数据预加载。
- onStop():Activity完全不可见。可以在这里停止动画、释放大对象。
为什么会这样?因为从onStart到onStop之间,用户可能只是短暂离开。比如接了个电话,这时候你不需要重新创建Activity,只需要恢复界面状态就行。
3.2.3 前台与后台组
- onResume():Activity进入前台,可以跟用户交互了。通常在这里开启摄像头、传感器等。
- onPause():Activity失去焦点,但部分可见。保存用户输入的数据、暂停动画。
嗯,这里要注意:onPause里不能做耗时操作,因为它执行完下一个Activity才能显示。我曾经在onPause里写了个数据库写入操作,结果界面切换卡了半秒,被产品经理追着改。
onCreate → onStart → onResume → (运行中) → onPause → onStop → onDestroy
常见分支路径:
按Home键:onPause → onStop
按返回键:onPause → onStop → onDestroy
从后台恢复:onRestart → onStart → onResume
3.3 四种启动模式
启动模式解决的是「Activity怎么入栈」的问题。Android用任务栈来管理Activity,默认是后进先出。但实际开发中,我们经常需要改变这个规则。
我画了一张图,帮你理解四种启动模式的区别:
3.2.1 Standard(标准模式)
默认模式。每次启动都创建新实例,不管栈里有没有。我刚开始写Android时,用Standard模式启动了一个页面,用户点了五次按钮,按返回键就要按五次才能退出。这就是典型的「栈堆积」问题。
3.2.2 SingleTop(栈顶复用模式)
如果要启动的Activity已经在栈顶,直接复用,不走onCreate,而是走onNewIntent。适合通知栏点击跳转的场景——用户点一次通知进页面,再点一次不应该创建新页面。
3.2.3 SingleTask(栈内复用模式)
栈内只允许有一个实例。如果已经存在,会把它上面的Activity全部出栈,然后调用onNewIntent。我一般在主页面用这个模式,防止用户从其他页面跳回主页时产生多个主页实例。
3.2.4 SingleInstance(单实例模式)
这个最特殊。它单独占一个任务栈,整个系统里只有一个实例。适合那种「全局唯一」的页面,比如来电界面、闹钟响铃界面。嗯,这个模式用得少,但用对了很管用。
3.4 Intent与Activity跳转
Intent是Android里连接组件的「信使」。说白了,你想启动一个Activity,就得通过Intent告诉系统:你要去哪,带什么东西。
3.4.1 显式Intent
直接指定目标Activity的类名。最常见的方式:
// 从当前Activity跳转到SecondActivity
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
3.4.2 隐式Intent
不指定具体类名,而是通过action、category等条件让系统帮你匹配。适合跨应用跳转:
// 打开浏览器
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://www.example.com"));
startActivity(intent);
3.4.3 携带数据跳转
用putExtra传数据,用getXXXExtra接收:
// 发送方
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("user_id", 1001);
intent.putExtra("user_name", "张三");
startActivity(intent);
// 接收方(在DetailActivity的onCreate中)
int userId = getIntent().getIntExtra("user_id", 0);
String userName = getIntent().getStringExtra("user_name");
3.4.4 启动Activity并获取结果
有时候你需要从目标页面带回数据,比如选照片、填表单:
// 启动
Intent intent = new Intent(this, PickActivity.class);
startActivityForResult(intent, REQUEST_CODE_PICK);
// 在目标页面设置结果
Intent resultIntent = new Intent();
resultIntent.putExtra("selected_item", "item_1");
setResult(RESULT_OK, resultIntent);
finish();
// 在当前页面接收结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_PICK && resultCode == RESULT_OK) {
String item = data.getStringExtra("selected_item");
// 处理选中的结果
}
}
嗯,这里要注意:startActivityForResult在Android 11以上已经标记为deprecated,官方推荐用Activity Result API。不过老项目里还是经常见到,你得能看懂。
- 四种状态:运行、暂停、停止、销毁。系统根据优先级管理Activity
- 七个回调:onCreate/onDestroy做初始化/释放,onStart/onStop管可见性,onResume/onPause管前台交互
- 四种启动模式:Standard默认新建,SingleTop栈顶复用,SingleTask栈内唯一,SingleInstance独立栈
- Intent跳转:显式指定类名,隐式靠条件匹配,putExtra传数据,startActivityForResult拿结果
Activity这块内容,说实话,你写三五个项目之后自然就熟了。但刚开始学的时候,一定要动手写代码验证。我建议你写个Demo,在每个回调里打印日志,然后各种操作(按Home键、旋转屏幕、跳转页面),看看日志输出顺序。这样印象最深。
公众号:蓝海资料掘金营,微信deep3321