一、Activity 是什么
Activity 是 Android 四大组件(Activity、Service、BroadcastReceiver、ContentProvider)中唯一负责与用户交互的组件。它提供了一个窗口(Window),用于绘制 UI 并接收用户的触摸、按键等输入事件。
1.1 继承体系
java.lang.Object
└─ android.content.Context ← 提供应用环境接口
└─ android.content.ContextWrapper ← 包装代理模式
└─ android.view.ContextThemeWrapper ← 主题支持
└─ android.app.Activity ← 核心实现
Activity 间接继承自 Context,因此它具备访问资源、启动其他组件、发送广播等能力。同时它实现了 Window.Callback、KeyEvent.Callback 等接口,负责接收窗口和按键事件。
1.2 核心职责
| 职责 | 说明 |
|---|---|
| UI 容器 | 通过 setContentView() 加载布局,管理 View 树 |
| 生命周期管理 | 系统通过回调方法通知 Activity 状态变化 |
| 任务栈管理 | 参与 Task 和 Back Stack 的调度 |
| 数据传递 | 通过 Intent 和 Bundle 在组件间传递数据 |
| 配置响应 | 响应屏幕旋转、语言切换等配置变更 |
二、生命周期详解
Activity 生命周期由 7 个核心回调 构成,系统在不同状态转换时触发对应方法。
2.1 完整生命周期图
┌─────────────┐
│ onCreate │ ← 实例创建,初始化 UI
└──────┬──────┘
▼
┌─────────────┐
│ onStart │ ← 可见但不可交互
└──────┬──────┘
▼
┌─────────────┐
│ onResume │ ← 前台,可交互 ★
└──────┬──────┘
│
┌────────────────────────────┼────────────────────────────┐
│ 其他 Activity 部分覆盖 │ 其他 Activity 完全覆盖 │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ onPause │ │ onPause │ │ │
│ (仍可见) │ └─────┬─────┘ │ │
└─────┬─────┘ ▼ │ │
│ ┌───────────┐ │ │
│ │ onStop │ ← 完全不可见 │ │
│ │ (后台状态) │ │ │
│ └─────┬─────┘ │ │
│ │ │ │
│ ┌────────────┘ │ │
│ ▼ │ │
│ ┌───────────┐ │ │
│ │ onRestart │ ← 从后台回到前台 │ │
│ └─────┬─────┘ │ │
│ │ │ │
└─────────────┘ │ │
▼ │
┌───────────┐ │
│ onDestroy │ ← 销毁
└───────────┘ │
│
←─────────────────────────────────────────────────────────────────────┘
返回/退出
2.2 各回调方法详解
| 回调 | 触发时机 | 典型操作 | 调用次数 |
|---|---|---|---|
onCreate() |
Activity 实例首次创建,PhoneWindow、ContextImpl 赋值完成后 | setContentView()、初始化变量、恢复 savedInstanceState |
1 次 |
onStart() |
Activity 即将进入前台可见状态 | 注册广播、启动动画 | 多次 |
onResume() |
Activity 进入前台,可接收用户输入 | 获取焦点、启动相机/定位等需要前台权限的操作 | 多次 |
onPause() |
Activity 失去焦点(被部分覆盖或弹窗) | 保存关键数据、停止动画、释放相机 | 多次 |
onStop() |
Activity 完全不可见 | 注销广播、释放资源、停止定位 | 多次 |
onRestart() |
从 onStop 状态回到前台时 |
刷新数据 | 多次 |
onDestroy() |
Activity 被销毁前 | 释放所有资源、取消网络请求、解绑服务 | 1 次 |
2.3 关键状态定义
- Active/Running:onResume() 后,位于前台,可与用户交互
- Paused:onPause() 后,失去焦点,但仍部分可见(如被透明/对话框 Activity 覆盖)
- Stopped:onStop() 后,完全不可见,进入后台
- Killed/Destroyed:onDestroy() 后,等待 GC 回收
2.4 生命周期回调顺序(典型场景)
| 场景 | A 的生命周期 | B 的生命周期 |
|---|---|---|
| A 启动 B | onPause() → onStop() |
onCreate() → onStart() → onResume() |
| 按返回键从 B 回到 A | --- | onPause() → onStop() → onDestroy() |
onRestart() → onStart() → onResume() |
--- | |
| 按 Home 键 | onPause() → onStop() |
--- |
| 从最近任务恢复 | onRestart() → onStart() → onResume() |
--- |
| 屏幕旋转 | onPause() → onStop() → onDestroy() → onCreate() → onStart() → onResume() |
--- |
重要:新 Activity 的 onResume() 总是在旧 Activity 的 onPause() 之后执行。系统保证这两个回调的时序,以确保前台 Activity 始终只有一个。
三、启动模式与任务栈
3.1 Task(任务)与 Back Stack(返回栈)
Task 是用户在执行某项任务时与之交互的 Activity 集合,这些 Activity 按照打开顺序排列在 Back Stack(返回栈) 中。栈顶的 Activity 是当前正在显示的页面,按返回键时栈顶 Activity 出栈并销毁。
3.2 四种启动模式
在 AndroidManifest.xml 中通过 android:launchMode 配置:
| 启动模式 | 行为 | 适用场景 |
|---|---|---|
| standard(默认) | 每次启动都创建新实例,入栈 | 普通页面 |
| singleTop | 栈顶复用:若目标在栈顶,调用 onNewIntent() 而非创建新实例;不在栈顶则新建 |
通知点击跳转、搜索页面 |
| singleTask | 栈内复用:整个任务栈中只有一个实例。启动时若已存在,将其上方所有 Activity 清除,并调用 onNewIntent() |
应用首页、WebView 页面 |
| singleInstance | 独占一个任务栈,栈中只有该 Activity 一个实例 | 来电界面、视频通话 |
3.2.1 singleTask 的 "clearTop" 效果
当前栈:A → B → C → D
启动 B(singleTask)后:
结果栈:A → B (C、D 被清除)
3.2.2 singleTop 的 onNewIntent()
当栈顶复用时,Activity 不会被重建,系统通过 onNewIntent(Intent) 传递新的 Intent:
java
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent); // 重要:更新 Intent,否则 getIntent() 仍是旧的
// 处理新数据
}
3.3 taskAffinity(任务亲和性)
taskAffinity 定义了 Activity 倾向于归属哪个 Task,默认值为应用包名。
单独设置 taskAffinity 不会生效,必须配合以下条件之一:
| 条件 | 效果 |
|---|---|
launchMode="singleTask" |
Activity 启动时,若不存在对应 affinity 的 Task,则新建 Task |
launchMode="singleInstance" |
强制新建独立 Task |
Intent.FLAG_ACTIVITY_NEW_TASK |
配合不同 taskAffinity 开启新 Task |
allowTaskReparenting="true" |
Activity 可从启动它的 Task 迁移到 affinity 匹配的 Task |
xml
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:taskAffinity="com.example.main" />
3.4 Intent Flag(动态控制)
Intent Flag 的优先级高于 Manifest 中的静态配置:
| Flag | 作用 |
|---|---|
FLAG_ACTIVITY_NEW_TASK |
在新 Task 中启动(通常配合 taskAffinity) |
FLAG_ACTIVITY_CLEAR_TOP |
若目标 Activity 存在,清除其上所有 Activity 并复用目标 |
FLAG_ACTIVITY_SINGLE_TOP |
同 singleTop 效果 |
FLAG_ACTIVITY_CLEAR_TASK |
清除整个 Task,目标 Activity 作为新根 |
FLAG_ACTIVITY_NO_HISTORY |
启动后不保留在栈中 |
典型用法:退出登录后跳转登录页
kotlin
val intent = Intent(this, LoginActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intent)
四、Activity 启动流程(Framework 层)
用户点击图标 / startActivity()
│
▼
Instrumentation.execStartActivity() ← 应用进程
│
▼
ActivityTaskManagerService.startActivity() ← Binder IPC 到 system_server
│
▼
ATMS 进行权限检查、Intent 解析、启动模式计算
│
▼
ActivityStarter.execute() ← 决定如何启动(新实例?复用?新 Task?)
│
▼
RootWindowContainer / Task 管理栈状态
│
▼
如果需要创建新进程:
ActivityManagerService.startProcessLocked()
Zygote fork 新进程
│
▼
新进程启动后:
ActivityThread.main() → Looper.prepare() → Looper.loop()
attachApplication() 到 AMS
│
▼
ATMS 调度 Activity 启动
│
▼
ApplicationThread.scheduleTransaction() ← Binder 回调到应用进程
│
▼
ClientTransactionHandler.handleLaunchActivity()
│
▼
Instrumentation.newActivity() ← 反射创建 Activity 实例
│
▼
Activity.attach() ← 创建 PhoneWindow、WindowManager
│
▼
Instrumentation.callActivityOnCreate() ← 调用 onCreate()
│
▼
Activity.onCreate() → setContentView()
│
▼
WindowManager.addView(decorView) ← 将 DecorView 添加到 WMS
│
▼
Choreographer 触发 VSync → 测量/布局/绘制 → SurfaceFlinger 合成 → 显示
关键关联点:
- Instrumentation 是 Activity 启动的"指挥棒"
- ApplicationThread 是应用进程与 system_server 通信的 Binder 代理
- ActivityThread 是应用进程的主线程入口
- PhoneWindow 在 Activity.attach() 中被创建
五、Activity 与 Window / View 的关系
5.1 三者关系
Activity
├── PhoneWindow(Window 的实现类)
│ ├── DecorView(FrameLayout,根 View)
│ │ ├── TitleBar(系统标题栏,可隐藏)
│ │ └── ContentParent(FrameLayout,id = android.R.id.content)
│ │ └── 你的布局(通过 setContentView 添加)
│ │
│ └── WindowManager(管理窗口的添加、更新、移除)
│
└── Window.Callback(接收窗口事件,如 dispatchTouchEvent)
5.2 setContentView() 到底做了什么
java
// Activity.setContentView()
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID); // 委托给 PhoneWindow
initWindowDecorActionBar();
}
// PhoneWindow.setContentView()
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); // 1. 创建 DecorView + ContentParent
}
// 2. 将用户布局 inflate 到 ContentParent 中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
流程拆解:
- installDecor():若 DecorView 不存在,创建它(加载系统布局 screen_simple.xml 或 screen_title.xml)
- 找到 ContentParent(id 为 R.id.content 的 FrameLayout)
- 将用户的布局文件 inflate 到 ContentParent 中
5.3 从 onCreate 到显示在屏幕上
onCreate() 中调用 setContentView()
│
▼
布局被 inflate 到 ContentParent,但尚未测量/布局
│
▼
onResume() 执行完毕
│
▼
Activity.makeVisible() 被调用
│
▼
mDecor.setVisibility(View.VISIBLE)
│
▼
WindowManager.addView(mDecor, params) ← 第一次真正将 DecorView 添加到 WMS
│
▼
ViewRootImpl 被创建,执行 requestLayout()
│
▼
performTraversals():measure → layout → draw
│
▼
Surface 锁定 Canvas → Skia/OpenGL 绘制 → SurfaceFlinger 合成 → 屏幕显示
关键 :setContentView() 只是将布局加载到内存中的 View 树,真正的测量、布局、绘制发生在 onResume() 之后、Window 首次变为可见时。
六、数据传递与返回
6.1 显式启动
kotlin
val intent = Intent(this, TargetActivity::class.java)
intent.putExtra("key", "value")
startActivity(intent)
6.2 隐式启动
通过 Action、Category、Data 匹配:
xml
<activity android:name=".ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
kotlin
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, "分享内容")
}
startActivity(intent)
6.3 带返回值的启动
kotlin
// A 启动 B
val intent = Intent(this, BActivity::class.java)
startActivityForResult(intent, REQUEST_CODE) // 已废弃,推荐使用 Activity Result API
// B 返回数据
val resultIntent = Intent().apply {
putExtra("result", "data")
}
setResult(Activity.RESULT_OK, resultIntent)
finish()
// A 接收返回
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
val result = data?.getStringExtra("result")
}
}
现代推荐方式(Activity Result API):
kotlin
private val launcher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
val data = result.data?.getStringExtra("result")
}
}
// 使用
launcher.launch(Intent(this, BActivity::class.java))
七、配置变更与状态保存
7.1 配置变更类型
- 屏幕旋转(orientation)
- 键盘可用性变化(keyboardHidden)
- 屏幕尺寸变化(screenSize)
- 夜间模式切换(uiMode)
- 语言切换(locale)
默认情况下,配置变更会导致 Activity 销毁并重建(走完整的 onDestroy() → onCreate() 流程)。
7.2 状态保存与恢复
java
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("edit_text", editText.getText().toString());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String text = savedInstanceState.getString("edit_text");
editText.setText(text);
}
}
保存时机 :onSaveInstanceState() 在 onStop() 之前调用(API 28+ 在 onStop() 之后),用于保存瞬时 UI 状态 。
恢复时机:onCreate(Bundle) 或 onRestoreInstanceState(Bundle)。
7.3 自行处理配置变更
若不想重建 Activity,可在 Manifest 中声明:
xml
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden" />
此时配置变更时,系统不重建 Activity,而是调用:
java
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
// 手动处理横屏布局
}
}
八、Activity 的注册要求
Activity必须在AndroidManifest.xml中声明,否则启动时会抛出ActivityNotFoundException。
xml
<manifest ...>
<application ...>
<!-- 入口 Activity -->
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 普通 Activity -->
<activity android:name=".SecondActivity" />
</application>
</manifest>
- MAIN + LAUNCHER:标识应用的入口 Activity,桌面图标点击后启动
- exported="true"(Android 12+ 强制要求):允许外部应用启动该 Activity
九、总结
| 维度 | 核心要点 |
|---|---|
| 本质 | 用户交互的窗口容器,管理 View 树和生命周期 |
| 生命周期 | 7 个核心回调,掌握状态转换和时序关系 |
| 启动模式 | 4 种模式控制实例复用和栈行为,taskAffinity 控制 Task 归属 |
| 启动流程 | Instrumentation → ATMS → ActivityStarter → ApplicationThread → ActivityThread |
| UI 链路 | setContentView() → PhoneWindow → DecorView → WindowManager → ViewRootImpl → 屏幕 |
| 数据传递 | Intent + Bundle,显式/隐式启动,Activity Result API |
| 配置变更 | 默认重建,可通过 configChanges 自行处理或配合 onSaveInstanceState() |