目录
一、Activity 基础认知:到底什么是 Activity?
1.3 Activity 的 “容器特性”:任务栈(Task Stack)
二、生命周期深度解析:7 个回调 + 6 个场景,再也不踩坑
场景 3:从 SecondActivity 按返回键回到原 Activity
三、启动模式全解析:4 种模式 + 实战场景,避免返回键混乱
四、Intent 与 Activity 通信:显式 / 隐式启动 + 数据传递全攻略
1. Parcelable(高效,Android 原生支持)
4.3 带返回值的跳转:startActivityForResult
示例 7:用 Activity Result API 实现带返回值跳转
示例 8:用 onSaveInstanceState 保存 EditText 内容

class 卑微码农:
def __init__(self):
self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
self.发量 = 100 # 初始发量
self.咖啡因耐受度 = '极限'
def 修Bug(self, bug):
try:
# 试图用玄学解决问题
if bug.严重程度 == '离谱':
print("这一定是环境问题!")
else:
print("让我看看是谁又没写注释...哦,是我自己。")
except Exception as e:
# 如果try块都救不了,那就...
print("重启一下试试?")
self.发量 -= 1 # 每解决一个bug,头发-1
# 实例化一个我
我 = 卑微码农()
一、Activity 基础认知:到底什么是 Activity?

1.1 一句话搞懂 Activity 的核心作用
Activity 是 Android 系统提供的 “用户交互容器”,本质上是一个可视化的界面载体。你打开某信的聊天页、刷某音的视频页、用某宝的下单页,每个独立的交互界面,都是一个 Activity 实例在工作。
简单说:Activity 负责 “展示界面” 和 “处理用户交互” —— 按钮点击、输入文本、滑动屏幕等操作,都需要通过 Activity 来响应;而界面上的文字、图片、按钮等控件,也需要依附于 Activity 才能显示。
1.2 Activity 与其他组件的关系
Android 四大组件(Activity、Service、BroadcastReceiver、ContentProvider)各司其职,Activity 是其中唯一直接面向用户的组件,和其他组件的配合关系很清晰:
- 与 Service:Activity 负责前台交互,Service 负责后台任务(比如 Activity 触发音乐播放,Service 在后台持续播放);
- 与 BroadcastReceiver:Activity 可发送广播(比如登录成功后发送广播),也可接收广播(比如接收网络状态变化通知);
- 与 ContentProvider:Activity 通过 ContentProvider 获取数据(比如读取系统联系人、相册图片)。
1.3 Activity 的 “容器特性”:任务栈(Task Stack)
Android 用 “任务栈” 管理多个 Activity,就像叠盘子一样,遵循 “先进后出” 原则:
- 启动新 Activity 时,新实例会被 “压入” 栈顶,成为当前显示的界面;
- 按返回键时,栈顶的 Activity 会被 “弹出” 并销毁,上一个 Activity 回到栈顶;
- 所有 Activity 都弹出后,任务栈为空,应用退出。
比如打开 “某信→聊天列表→聊天窗口” 的流程:
- 启动某信,MainActivity(主界面)入栈,栈内:[MainActivity];
- 点击聊天列表,ChatListActivity 入栈,栈内:[MainActivity, ChatListActivity];
- 点击某条聊天,ChatActivity 入栈,栈内:[MainActivity, ChatListActivity, ChatActivity];
- 连续按 3 次返回键,依次弹出 ChatActivity、ChatListActivity、MainActivity,应用退出。
这个机制很重要,后面讲启动模式时会频繁用到。
二、生命周期深度解析:7 个回调 + 6 个场景,再也不踩坑

Activity 的生命周期是核心中的核心,也是新手最容易出错的地方。很多 bug(比如数据丢失、内存泄漏)都和没理解生命周期有关。
2.1 先记牢:7 个核心生命周期回调方法
Activity 从创建到销毁,会经历一系列固定的 “状态变化”,系统会通过回调方法通知我们。这 7 个方法必须熟记,每个方法的作用和调用时机都很明确:
| 方法名 | 调用时机 | 核心作用 | 注意事项 |
|---|---|---|---|
| onCreate() | Activity 首次创建时(仅调用 1 次) | 初始化布局、绑定控件、初始化数据 | 必须调用 setContentView () 加载布局,避免耗时操作 |
| onStart() | Activity 即将可见时 | 启动动画、注册非前台监听器 | 此时界面还未获取焦点,不能交互 |
| onResume() | Activity 可见且可交互时 | 启动前台服务、恢复播放、获取焦点 | 进入 “运行状态”,是交互的核心阶段 |
| onPause() | Activity 即将失去焦点(部分可见) | 暂停动画、保存临时数据、释放部分资源 | 执行速度要快,不能阻塞(影响新界面显示) |
| onStop() | Activity 完全不可见时 | 释放大内存资源、注销监听器 | 避免持有重量级对象(如相机、传感器) |
| onRestart() | Activity 停止后重新启动前 | 恢复之前的状态(如刷新数据) | 仅在 “停止后重启” 时触发(比如返回键回到该页面) |
| onDestroy() | Activity 销毁前(仅调用 1 次) | 彻底释放所有资源、取消异步任务 | 不能依赖此方法保存数据(系统可能直接杀死进程) |
2.2 用代码实测:生命周期的执行顺序
光看表格不够直观,我们写个简单示例,通过日志打印实际执行流程。
示例 1:生命周期回调日志打印
- 新建一个 Activity(LifeCycleActivity),重写所有生命周期方法:
public class LifeCycleActivity extends AppCompatActivity {
private static final String TAG = "LifeCycleTest";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_life_cycle);
Log.d(TAG, "onCreate:Activity创建");
// 初始化控件(示例:绑定一个按钮)
Button btnJump = findViewById(R.id.btn_jump);
btnJump.setOnClickListener(v -> {
// 跳转至新Activity
startActivity(new Intent(this, SecondActivity.class));
});
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart:Activity即将可见");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume:Activity可交互");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause:Activity即将失去焦点");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop:Activity完全不可见");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart:Activity准备重启");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy:Activity即将销毁");
}
}
- 新建 SecondActivity(仅作为跳转目标,无需复杂逻辑);
- 运行程序,观察 Logcat 日志,记录不同操作下的回调顺序。
2.3 6 个核心场景:生命周期到底怎么执行?
结合示例 1 的日志,我们分析 6 个实际开发中最常见的场景,彻底搞懂生命周期的执行逻辑:
场景 1:首次启动 Activity
- 执行顺序:onCreate () → onStart () → onResume ()
- 日志输出:
D/LifeCycleTest: onCreate:Activity创建
D/LifeCycleTest: onStart:Activity即将可见
D/LifeCycleTest: onResume:Activity可交互
- 说明:Activity 从无到有,完成初始化并进入可交互状态。
场景 2:点击按钮跳转至 SecondActivity
- 执行顺序(当前 Activity):onPause () → onStop ()
- 日志输出:
D/LifeCycleTest: onPause:Activity即将失去焦点
D/LifeCycleTest: onStop:Activity完全不可见
- 说明:当前 Activity 被新页面完全覆盖,先失去焦点,再变为不可见。
场景 3:从 SecondActivity 按返回键回到原 Activity
- 执行顺序:onRestart () → onStart () → onResume ()
- 日志输出:
D/LifeCycleTest: onRestart:Activity准备重启
D/LifeCycleTest: onStart:Activity即将可见
D/LifeCycleTest: onResume:Activity可交互
- 说明:Activity 从 “停止状态” 恢复,先重启,再可见,最后可交互。
场景 4:按 Home 键返回桌面
- 执行顺序:onPause () → onStop ()
- 日志输出:
D/LifeCycleTest: onPause:Activity即将失去焦点
D/LifeCycleTest: onStop:Activity完全不可见
- 说明:应用进入后台,Activity 并未销毁,只是停止状态。
场景 5:从桌面重新打开应用
- 执行顺序:onRestart () → onStart () → onResume ()
- 说明:和 “从 SecondActivity 返回” 逻辑一致,恢复后台应用。
场景 6:按返回键退出 Activity
- 执行顺序:onPause () → onStop () → onDestroy ()
- 日志输出:
D/LifeCycleTest: onPause:Activity即将失去焦点
D/LifeCycleTest: onStop:Activity完全不可见
D/LifeCycleTest: onDestroy:Activity即将销毁
- 说明:Activity 被销毁,资源释放。
特殊场景:屏幕旋转(配置变更)
默认情况下,屏幕旋转会导致 Activity 销毁重建,生命周期执行:onPause () → onStop () → onDestroy () → onCreate () → onStart () → onResume ()
- 问题:此时 EditText 中的输入内容、临时变量会丢失;
- 解决方案:后面会讲 onSaveInstanceState () 和 ViewModel 的用法。
2.4 生命周期的核心原则:“资源对称”
这是我踩了很多坑后总结的关键原则:在哪个方法申请的资源,就在对应的方法中释放,避免内存泄漏。
常见的资源对称操作:
| 申请资源(方法) | 释放资源(方法) | 示例操作 |
|---|---|---|
| onCreate() | onDestroy() | 初始化数据库、创建单例对象 |
| onStart() | onStop() | 注册广播接收器、启动位置监听 |
| onResume() | onPause() | 启动动画、恢复音视频播放 |
反例:在 onCreate () 中注册广播接收器,却没在 onDestroy () 中注销,会导致 Activity 实例被广播持有,无法回收,造成内存泄漏。
三、启动模式全解析:4 种模式 + 实战场景,避免返回键混乱

Activity 的启动模式决定了新 Activity 实例如何创建、如何加入任务栈,直接影响返回键逻辑和用户体验。比如 “微信主界面无论怎么跳转,返回都能回到主界面”,就是用对了启动模式。
3.1 4 种启动模式的核心区别
Android 提供 4 种启动模式,可通过 AndroidManifest.xml 的android:launchMode属性配置,也可通过 Intent 的 Flags 动态设置(动态设置优先级更高)。
1. standard(默认模式:标准模式)
- 核心规则:每次启动都创建新实例,不管栈中是否已存在;
- 任务栈行为:新实例压入 “启动它的 Activity 所在的栈”;
- 示例:打开新闻 App,多次点击 “详情页”,会创建多个详情页实例,按返回键需依次退出;
- 适用场景:大多数普通页面(如新闻详情、商品详情);
- 注意点:非 Activity 上下文(如 Service)启动时,需添加
FLAG_ACTIVITY_NEW_TASK,否则报错。
2. singleTop(栈顶复用模式)
- 核心规则:如果目标 Activity 已在栈顶,直接复用,不创建新实例;若不在栈顶,仍创建新实例;
- 复用回调:复用时会触发
onNewIntent(Intent intent)方法(需重写此方法处理新数据); - 示例:搜索页,若当前已在搜索页(栈顶),再次点击搜索按钮,复用当前页面并刷新结果;
- 适用场景:通知跳转页、搜索结果页、消息详情页(避免栈顶重复创建)。
3. singleTask(栈内复用模式)
- 核心规则:栈内唯一实例,若栈中已存在该 Activity,会将其上方的所有 Activity 全部弹出,让它回到栈顶并复用;
- 任务栈行为:默认进入 “启动它的栈”,可通过
taskAffinity属性指定新栈; - 复用回调:触发
onNewIntent(Intent intent)方法; - 示例:微信主界面,无论从聊天页、朋友圈页怎么返回,都能直接回到主界面;
- 适用场景:应用主界面、购物车页面(确保全局唯一,返回逻辑清晰)。
4. singleInstance(单实例模式)
- 核心规则:全局唯一实例,且独占一个任务栈(栈内只有它自己);
- 任务栈行为:其他应用启动该 Activity 时,直接复用这个独立栈中的实例;
- 示例:系统电话界面、闹钟提醒界面(跨应用共享实例,避免重复创建);
- 适用场景:系统级界面、跨应用共享的功能页(如支付界面)。
3.2 启动模式对比表(一目了然)
| 启动模式 | 实例数量 | 任务栈特性 | 复用回调 | 典型场景 |
|---|---|---|---|---|
| standard | 多个 | 压入启动方所在栈 | 无 | 新闻详情、商品详情 |
| singleTop | 多个(栈顶唯一) | 压入启动方所在栈 | onNewIntent() | 搜索结果、通知跳转 |
| singleTask | 一个(栈内唯一) | 默认启动方栈,可指定新栈 | onNewIntent() | 应用主界面、购物车 |
| singleInstance | 一个(全局唯一) | 独占独立栈 | onNewIntent() | 系统电话、闹钟提醒 |
3.3 实战示例:singleTop 模式的复用逻辑
示例 2:singleTop 模式下的栈顶复用
- 在 AndroidManifest.xml 中配置启动模式:
<activity
android:name=".SingleTopActivity"
android:launchMode="singleTop" /> <!-- 配置singleTop模式 -->
- 重写 SingleTopActivity 的
onNewIntent方法:
public class SingleTopActivity extends AppCompatActivity {
private static final String TAG = "SingleTopTest";
private TextView tvData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_top);
tvData = findViewById(R.id.tv_data);
// 接收首次启动的数据
handleIntent(getIntent());
// 按钮:再次启动当前Activity
findViewById(R.id.btn_restart_self).setOnClickListener(v -> {
Intent intent = new Intent(this, SingleTopActivity.class);
intent.putExtra("data", "新数据:" + System.currentTimeMillis());
startActivity(intent);
});
}
// 复用实例时触发,处理新数据
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG, "onNewIntent:复用栈顶实例");
handleIntent(intent); // 重新处理新数据
}
// 处理Intent数据
private void handleIntent(Intent intent) {
if (intent != null && intent.hasExtra("data")) {
String data = intent.getStringExtra("data");
tvData.setText(data);
Log.d(TAG, "接收数据:" + data);
}
}
}
- 运行测试:
- 首次启动:执行 onCreate (),显示初始数据;
- 点击 “重新启动当前 Activity”:栈顶已存在该实例,触发 onNewIntent (),界面更新为新数据,未创建新实例;
- 日志输出:
onNewIntent:复用栈顶实例+ 新数据日志。
3.4 动态设置启动模式(Intent Flags)
除了在 Manifest 中配置,还可以通过 Intent 的 Flags 动态设置启动模式,更灵活:
FLAG_ACTIVITY_NEW_TASK:等同于 singleTask 模式(非 Activity 上下文必须用);FLAG_ACTIVITY_SINGLE_TOP:等同于 singleTop 模式;FLAG_ACTIVITY_CLEAR_TOP:清空目标 Activity 上方的所有 Activity(常与 singleTask 配合);FLAG_ACTIVITY_NO_HISTORY:Activity 退出后不保留在任务栈(如登录跳转页)。
示例:动态设置 singleTop 模式:
Intent intent = new Intent(this, TargetActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); // 动态设置
startActivity(intent);
四、Intent 与 Activity 通信:显式 / 隐式启动 + 数据传递全攻略

Activity 之间的跳转和数据传递,全靠 Intent(意图)实现。Intent 就像 “信使”,负责携带跳转目标和数据。
4.1 两种启动方式:显式启动 vs 隐式启动
1. 显式启动(常用,精准跳转)
直接指定目标 Activity 的全类名,明确告诉系统要启动哪个 Activity,适用于应用内跳转。
示例 3:显式启动并传递基本数据
// 发送方(MainActivity)
findViewById(R.id.btn_jump).setOnClickListener(v -> {
// 1. 创建Intent,指定上下文和目标Activity
Intent intent = new Intent(MainActivity.this, ReceiveActivity.class);
// 2. 携带数据(键值对形式,支持基本数据类型、String、Parcelable等)
intent.putExtra("name", "张三");
intent.putExtra("age", 25);
intent.putExtra("isStudent", true);
// 3. 启动Activity
startActivity(intent);
});
// 接收方(ReceiveActivity)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_receive);
// 获取Intent中的数据(第二个参数为默认值,防止数据为null)
Intent intent = getIntent();
String name = intent.getStringExtra("name", "未知");
int age = intent.getIntExtra("age", 0);
boolean isStudent = intent.getBooleanExtra("isStudent", false);
// 显示数据
TextView tvResult = findViewById(R.id.tv_result);
tvResult.setText("姓名:" + name + "\n年龄:" + age + "\n是否学生:" + isStudent);
}
2. 隐式启动(灵活,跨应用跳转)
不指定具体的 Activity 类名,而是通过 “Action、Category、Data” 等条件匹配,适用于跨应用跳转(如打开浏览器、拨打电话)或应用内组件解耦。
隐式启动的核心是 “匹配规则”:Intent 必须满足目标 Activity 在 Manifest 中声明的<intent-filter>条件。
示例 4:隐式启动应用内 Activity
- 在 Manifest 中为目标 Activity 配置 intent-filter:
<activity android:name=".ImplicitActivity">
<intent-filter>
<!-- Action:自定义动作(需唯一,建议用包名前缀) -->
<action android:name="com.example.action.VIEW_DETAIL" />
<!-- Category:默认类别(必须添加,否则匹配失败) -->
<category android:name="android.intent.category.DEFAULT" />
<!-- Data:数据格式限制(可选,如指定URL Scheme) -->
<data android:scheme="app" android:host="detail" />
</intent-filter>
</activity>
- 发送方通过条件匹配启动:
findViewById(R.id.btn_implicit_jump).setOnClickListener(v -> {
Intent intent = new Intent();
// 设置Action(与Manifest中一致)
intent.setAction("com.example.action.VIEW_DETAIL");
// 设置Data(与Manifest中scheme、host一致)
intent.setData(Uri.parse("app://detail?id=123"));
// 启动(建议先判断是否有Activity匹配,避免崩溃)
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(this, "无匹配的Activity", Toast.LENGTH_SHORT).show();
}
});
示例 5:跨应用隐式启动(打开浏览器)
无需配置,直接使用系统预设的 Action:
// 打开百度网页
findViewById(R.id.btn_open_browser).setOnClickListener(v -> {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW); // 系统预设Action:查看内容
intent.setData(Uri.parse("https://www.baidu.com")); // 网页URL
startActivity(intent);
});
// 拨打电话(需添加权限)
// 1. 在Manifest中添加权限:<uses-permission android:name="android.permission.CALL_PHONE" />
// 2. 代码:
findViewById(R.id.btn_make_call).setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "请授予拨打电话权限", Toast.LENGTH_SHORT).show();
return;
}
startActivity(intent);
});
4.2 数据传递进阶:传递对象与集合
基本数据类型满足不了复杂需求时,需要传递对象(如 User、Product),Android 支持两种序列化方式:Parcelable(推荐)和 Serializable。
1. Parcelable(高效,Android 原生支持)
示例 6:Parcelable 序列化传递对象
- 创建 User 类并实现 Parcelable 接口(可通过 Android Studio 自动生成):
public class User implements Parcelable {
private String name;
private int age;
private String address;
// 构造方法
public User(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
// 从Parcel中读取数据(顺序需与writeToParcel一致)
protected User(Parcel in) {
name = in.readString();
age = in.readInt();
address = in.readString();
}
// 必须实现的方法(直接返回0即可)
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
// 写入Parcel(顺序需与读取顺序一致)
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
dest.writeString(address);
}
@Override
public int describeContents() {
return 0;
}
// getter/setter方法
public String getName() { return name; }
public int getAge() { return age; }
public String getAddress() { return address; }
}
- 传递与接收对象:
// 发送方
User user = new User("李四", 30, "北京市海淀区");
Intent intent = new Intent(this, UserDetailActivity.class);
intent.putExtra("user", user); // 直接传递Parcelable对象
startActivity(intent);
// 接收方
User user = getIntent().getParcelableExtra("user");
if (user != null) {
tvUserInfo.setText("姓名:" + user.getName() + "\n年龄:" + user.getAge() + "\n地址:" + user.getAddress());
}
2. Serializable(简单,Java 原生支持)
实现 Serializable 接口即可,无需额外代码,但效率比 Parcelable 低,适合简单对象:
public class Product implements Serializable {
private String productName;
private double price;
// 构造方法、getter/setter
}
// 传递:intent.putExtra("product", new Product("手机", 2999));
// 接收:Product product = (Product) getIntent().getSerializableExtra("product");
4.3 带返回值的跳转:startActivityForResult
场景:从 AActivity 跳转到 BActivity,BActivity 处理完成后,返回数据给 AActivity(如选择图片后返回图片路径、登录后返回用户信息)。
注意:AndroidX 中推荐使用Activity Result API(替代旧的startActivityForResult),更简洁且无内存泄漏风险。
示例 7:用 Activity Result API 实现带返回值跳转
- 发送方(AActivity):
public class AActivity extends AppCompatActivity {
private TextView tvResult;
// 1. 注册ActivityResult回调
private ActivityResultLauncher<Intent> launcher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
// 3. 接收返回结果
if (result.getResultCode() == RESULT_OK) {
Intent data = result.getData();
if (data != null) {
String selectResult = data.getStringExtra("select_result");
tvResult.setText("选择结果:" + selectResult);
}
}
}
);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
tvResult = findViewById(R.id.tv_result);
// 2. 触发跳转
findViewById(R.id.btn_jump_to_b).setOnClickListener(v -> {
Intent intent = new Intent(this, BActivity.class);
launcher.launch(intent); // 用注册的launcher启动
});
}
}
- 接收方(BActivity):
public class BActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
// 选择数据后返回
findViewById(R.id.btn_select).setOnClickListener(v -> {
Intent resultIntent = new Intent();
resultIntent.putExtra("select_result", "选中了第3条数据");
// 设置返回结果(RESULT_OK表示成功)
setResult(RESULT_OK, resultIntent);
finish(); // 关闭当前Activity
});
}
}
五、数据保存与恢复:解决屏幕旋转、内存不足导致的数据丢失

实际开发中,Activity 可能因屏幕旋转、系统内存不足被销毁,导致临时数据(如 EditText 输入内容、列表滚动位置)丢失。这部分要掌握两种核心解决方案。
5.1 轻量级数据:onSaveInstanceState
系统在 Activity 异常销毁前(如屏幕旋转、内存不足),会自动调用onSaveInstanceState(Bundle outState)方法,允许我们保存临时数据。
示例 8:用 onSaveInstanceState 保存 EditText 内容
public class SaveStateActivity extends AppCompatActivity {
private EditText etInput;
private static final String KEY_INPUT = "key_input";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_save_state);
etInput = findViewById(R.id.et_input);
// 恢复数据( savedInstanceState不为null表示异常销毁后重建)
if (savedInstanceState != null) {
String inputText = savedInstanceState.getString(KEY_INPUT, "");
etInput.setText(inputText);
// 恢复光标位置
etInput.setSelection(inputText.length());
}
}
// 保存数据(异常销毁前调用)
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 保存EditText中的输入内容
outState.putString(KEY_INPUT, etInput.getText().toString());
}
}
5.2 重量级数据:ViewModel(推荐)
onSaveInstanceState 适合保存少量简单数据(如 String、int),对于复杂数据(如列表数据、网络请求结果),推荐用 ViewModel。ViewModel 的生命周期与 Activity 的配置变更无关,屏幕旋转时不会被销毁,数据自然保留。
示例 9:用 ViewModel 保存列表数据
- 添加依赖(AndroidX):
dependencies {
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
}
- 创建 ViewModel 类:
public class MyViewModel extends ViewModel {
// 保存列表数据(ViewModel销毁时才会丢失)
private MutableLiveData<List<String>> dataList = new MutableLiveData<>();
// 设置数据
public void setDataList(List<String> list) {
dataList.setValue(list);
}
// 获取数据(通过LiveData观察变化)
public LiveData<List<String>> getDataList() {
return dataList;
}
}
- 在 Activity 中使用:
public class ViewModelActivity extends AppCompatActivity {
private MyViewModel viewModel;
private ListView lvData;
private ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_model);
lvData = findViewById(R.id.lv_data);
// 1. 获取ViewModel实例(与Activity生命周期绑定)
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
// 2. 观察数据变化
viewModel.getDataList().observe(this, list -> {
if (list != null) {
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
lvData.setAdapter(adapter);
}
});
// 3. 模拟网络请求获取数据(屏幕旋转后数据不会丢失)
findViewById(R.id.btn_load_data).setOnClickListener(v -> {
List<String> data = new ArrayList<>();
data.add("数据1");
data.add("数据2");
data.add("数据3");
viewModel.setDataList(data); // 保存到ViewModel
});
}
}
5.3 禁止屏幕旋转(临时方案)
如果不需要适配横竖屏,可直接禁止屏幕旋转,避免 Activity 重建:
<activity
android:name=".NoRotateActivity"
android:screenOrientation="portrait" /> <!-- portrait:竖屏,landscape:横屏 -->
六、常见坑点与避坑指南(实战经验总结)

6.1 内存泄漏(最常见问题)
常见原因:
- 静态变量持有 Activity 引用(如
static Activity mActivity;); - 非静态内部类 / 匿名类持有 Activity(如 Handler、Thread 未及时销毁);
- 广播接收器、监听器未注销;
- 图片加载框架未取消请求(如 Glide 未调用
clear())。
解决方案:
- 避免用静态变量持有 Activity,如需使用 Context,优先用
getApplicationContext(); - Handler 使用静态内部类 + WeakReference:
// 正确的Handler写法
private static class MyHandler extends Handler {
private WeakReference<MyActivity> mActivityRef;
public MyHandler(MyActivity activity) {
mActivityRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MyActivity activity = mActivityRef.get();
if (activity != null && !activity.isFinishing()) {
// 处理消息
}
}
}
// 在onDestroy中移除消息
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
- 广播、监听器在对应的生命周期方法中注销;
- 用 LeakCanary 工具检测内存泄漏(集成后自动提示泄漏位置)。
6.2 启动模式误用导致的问题
- 问题 1:singleTask 模式导致 Activity 被异常销毁(如栈内已有实例,启动时会销毁上方 Activity);
- 解决方案:明确场景后使用,避免给普通页面设置 singleTask;
- 问题 2:standard 模式导致页面重复创建(如多次跳转详情页,返回键需多次点击);
- 解决方案:根据需求切换为 singleTop 模式。
6.3 隐式启动匹配失败崩溃
- 原因:Intent 的 Action、Category、Data 与目标 Activity 的 intent-filter 不匹配;
- 解决方案:启动前用
intent.resolveActivity(getPackageManager()) != null判断,避免崩溃。
6.4 onDestroy () 不执行
- 场景:系统内存不足时,可能直接杀死 Activity 进程,不调用 onDestroy ();
- 解决方案:不要在 onDestroy () 中保存关键数据(改用 onPause () 或 SharedPreferences),仅用于释放资源。
七、总结:Activity 核心知识点图谱
到这里,Activity 的核心内容已经全部讲完,我们用一张图谱梳理重点:
Activity核心知识点
├── 基础认知:界面载体、任务栈(先进后出)
├── 生命周期:7个回调+资源对称原则
├── 启动模式:4种模式+适用场景+onNewIntent
├── 数据传递:显式/隐式启动+Parcelable/Serializable+Activity Result API
├── 数据保存:onSaveInstanceState(轻量)+ ViewModel(重量)
├── 避坑指南:内存泄漏、启动模式误用、隐式启动匹配
其实 Activity 的学习没有捷径,关键是 “理解 + 实操”—— 把文中的示例逐个敲一遍,结合日志观察执行流程,再在实际项目中应用,很快就能熟练掌握。
359

被折叠的 条评论
为什么被折叠?



