四大组件 / 生命周期 / Context — 知识详解
更新: 5/15/2026 字数: 0 字 时长: 0 分钟
Fragment 与 Service 原理内容较多,已拆分为独立文件:
- fragment-service-deep.md — Fragment 双生命周期详解(add/replace/hide/show 对比)、Fragment 通信、Service 两种模式混合使用、前台 Service、Bound Service 与 AIDL
1. Activity 生命周期
1.1 正常生命周期
onCreate → onStart → onResume → [运行中] → onPause → onStop → onDestroyonCreate:创建 Activity,setContentView、初始化数据。参数savedInstanceState用于恢复状态onStart:可见但不可交互onResume:可见且可交互,前台状态onPause:部分可见(如弹出对话框 Activity),不要做耗时操作(影响下一个 Activity 显示)onStop:完全不可见onDestroy:销毁,释放资源
1.2 关键场景分析
A 启动 B:
A.onPause → B.onCreate → B.onStart → B.onResume → A.onStop注意:A.onPause 先执行,所以 onPause 中不能做耗时操作,否则 B 的显示会延迟。
B 返回 A:
B.onPause → A.onRestart → A.onStart → A.onResume → B.onStop → B.onDestroy配置变更(如旋转屏幕):
onPause → onStop → onSaveInstanceState → onDestroy → onCreate → onStart → onRestoreInstanceState → onResumeActivity 被销毁重建。数据保存在 onSaveInstanceState(Bundle),恢复在 onCreate 或 onRestoreInstanceState。
ViewModel 不受配置变更影响:ViewModel 存储在 ViewModelStore 中,ViewModelStore 通过 onRetainNonConfigurationInstance 在配置变更时保留。
1.3 异常销毁与恢复
系统内存不足时可能杀死后台 Activity。恢复时:
onSaveInstanceState保存的 Bundle 会传给onCreate- View 系统自动保存/恢复有 id 的 View 的状态(EditText 文本、ScrollView 位置等)
- 大数据用
SavedStateHandle(ViewModel 中)或持久化存储
2. Activity 启动模式
2.1 四种启动模式
standard(默认):每次启动创建新实例,放入启动它的 Task 栈顶。
singleTop:栈顶复用。如果目标 Activity 已在栈顶,不创建新实例,调用 onNewIntent。不在栈顶则创建新实例。
- 场景:通知点击打开详情页(避免重复创建)
singleTask:栈内复用。在目标 Task 中查找,如果已存在则将其上方的 Activity 全部出栈(clearTop),调用 onNewIntent。
- 场景:首页 Activity(从任何地方回到首页,清除中间页面)
singleInstance:独占 Task。Activity 独占一个 Task 栈,整个系统中只有一个实例。
- 场景:来电界面、系统级页面
2.2 源码级分析
启动模式的处理在 AMS(ActivityManagerService)中:
TaskRecord:代表一个 Task 栈ActivityRecord:代表一个 Activity 实例ActivityStack:管理多个 TaskRecord
singleTask 的查找逻辑:
- 遍历所有 TaskRecord,查找是否有匹配的 ActivityRecord
- 找到则将该 Task 移到前台,清除目标 Activity 上方的所有 Activity
- 调用目标 Activity 的
onNewIntent
2.3 Intent Flags
// 等同于 singleTop
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// 清除目标 Activity 上方的所有 Activity(配合 singleTask 效果)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 创建新 Task
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 清除 Task 中所有 Activity,重新创建目标 Activity
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);3. Fragment
3.1 生命周期
Fragment 有自己的生命周期,且与宿主 Activity 关联:
onAttach → onCreate → onCreateView → onViewCreated → onViewStateRestored
→ onStart → onResume → [运行中]
→ onPause → onStop → onDestroyView → onDestroy → onDetach关键区别:
onCreateView:创建 Fragment 的 View 层级onViewCreated:View 已创建,可以安全地 findViewByIdonDestroyView:View 被销毁但 Fragment 实例可能还在(如加入 back stack)
3.2 ViewLifecycleOwner
Fragment 有两个生命周期:
- Fragment 自身生命周期(onCreate → onDestroy)
- View 生命周期(onCreateView → onDestroyView)
重要:在 Fragment 中观察 LiveData/Flow 时,应该使用 viewLifecycleOwner 而非 this:
// ✅ 正确:使用 viewLifecycleOwner
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.data.observe(viewLifecycleOwner) { data ->
// 更新 UI
}
}
// ❌ 错误:使用 this(Fragment 生命周期)
// Fragment 加入 back stack 后 View 被销毁但 Fragment 还在
// 返回时重新创建 View,但旧的 Observer 还在,导致重复观察3.3 Fragment 通信
// 1. ViewModel 共享(推荐)
// Activity 和多个 Fragment 共享同一个 ViewModel
val sharedViewModel: SharedViewModel by activityViewModels()
// 2. Fragment Result API(Jetpack 推荐)
// 发送方
setFragmentResult("requestKey", bundleOf("data" to "value"))
// 接收方
setFragmentResultListener("requestKey") { _, bundle ->
val data = bundle.getString("data")
}
// 3. 接口回调(传统方式)
interface OnItemClickListener {
fun onItemClick(item: Item)
}4. Service
4.1 两种启动方式
startService:
onCreate → onStartCommand → [运行中] → onDestroy- 调用
stopSelf()或stopService()停止 onStartCommand返回值决定被杀后的重启策略:START_NOT_STICKY:不重启START_STICKY:重启但 Intent 为 nullSTART_REDELIVER_INTENT:重启并重新传递最后一个 Intent
bindService:
onCreate → onBind → [客户端绑定中] → onUnbind → onDestroy- 返回 IBinder 供客户端通信
- 所有客户端解绑后自动销毁
- 可以同时 start + bind,需要同时 stop + unbind 才会销毁
4.2 前台 Service
Android 8.0+ 后台 Service 限制严格,长时间运行需要前台 Service:
class MyForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("运行中")
.setSmallIcon(R.drawable.ic_notification)
.build()
startForeground(NOTIFICATION_ID, notification)
return START_STICKY
}
}Android 12+ 需要声明 android:foregroundServiceType。 Android 14+ 进一步限制,必须指定具体类型(dataSync/location/mediaPlayback 等)。
5. BroadcastReceiver
5.1 注册方式
静态注册(AndroidManifest):
- 应用未启动也能接收(Android 8.0+ 大部分隐式广播不再支持静态注册)
- 适合:开机广播、应用安装/卸载广播
动态注册(代码):
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 处理广播,不能做耗时操作(10秒 ANR 限制)
}
}
registerReceiver(receiver, IntentFilter("com.example.MY_ACTION"))
// 必须在适当时机 unregisterReceiver 避免泄漏5.2 有序广播 vs 普通广播
- 普通广播(
sendBroadcast):异步分发,所有接收者几乎同时收到 - 有序广播(
sendOrderedBroadcast):按优先级依次分发,可以拦截(abortBroadcast)
5.3 本地广播
LocalBroadcastManager(已废弃)→ 推荐用 LiveData、Flow 或 EventBus 替代。
6. ContentProvider
6.1 作用
跨进程数据共享的标准接口。即使在同一个应用内,也可以用 ContentProvider 封装数据访问。
6.2 启动时序
ContentProvider 在 Application.onCreate 之前初始化:
Application.attachBaseContext
→ ContentProvider.onCreate(所有 Provider)
→ Application.onCreate这就是为什么很多库(如 Firebase、WorkManager)用 ContentProvider 做自动初始化——不需要用户手动在 Application 中调用 init。但这也会拖慢启动速度。
Jetpack App Startup 库通过合并多个 ContentProvider 为一个来优化启动性能。
6.3 跨进程原理
ContentProvider 底层通过 Binder 通信。ContentResolver 是客户端代理,通过 AMS 查找目标 Provider 的 Binder 引用,然后直接跨进程调用。
大数据传输(如文件)通过 ParcelFileDescriptor 传递文件描述符,避免 Binder 传输大小限制(1MB)。
7. Context 体系
7.1 继承关系
Context (抽象类)
├── ContextImpl (真正的实现)
├── ContextWrapper (装饰器)
│ ├── Application
│ ├── Service
│ └── ContextThemeWrapper
│ └── Activity7.2 不同 Context 的能力差异
| 操作 | Application | Activity | Service |
|---|---|---|---|
| 启动 Activity | ✅(需 NEW_TASK) | ✅ | ✅(需 NEW_TASK) |
| 弹 Dialog | ❌ | ✅ | ❌ |
| Layout Inflation | ⚠️(无主题) | ✅ | ⚠️(无主题) |
| 启动 Service | ✅ | ✅ | ✅ |
| 发送广播 | ✅ | ✅ | ✅ |
| 访问资源 | ✅ | ✅ | ✅ |
核心原则:
- 涉及 UI 的操作用 Activity Context
- 生命周期长的操作(如单例持有)用 Application Context,避免内存泄漏
getApplicationContext()返回 Application 实例getBaseContext()返回 ContextWrapper 内部的 ContextImpl
7.3 常见坑
// ❌ 内存泄漏:单例持有 Activity Context
object Manager {
lateinit var context: Context // 如果传入 Activity,Activity 无法被回收
}
// ✅ 正确:使用 Application Context
object Manager {
lateinit var context: Context
fun init(context: Context) {
this.context = context.applicationContext
}
}8. Activity Result API
替代 startActivityForResult + onActivityResult(已废弃):
// 注册(在 onCreate 之前,通常作为成员变量)
val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
val data = result.data
// 处理结果
}
}
// 启动
launcher.launch(Intent(this, SecondActivity::class.java))
// 常用内置 Contract
ActivityResultContracts.TakePicture() // 拍照
ActivityResultContracts.PickContact() // 选择联系人
ActivityResultContracts.RequestPermission() // 请求单个权限
ActivityResultContracts.RequestMultiplePermissions() // 请求多个权限
ActivityResultContracts.GetContent() // 选择文件与旧方式的对比:
旧方式 startActivityForResult 需要开发者手动定义 requestCode,所有结果都回到同一个 onActivityResult 方法中,用 requestCode 做 switch 区分:
// 旧方式:所有结果集中在一个回调,用 requestCode 区分
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
100 -> { /* 相机 */ }
200 -> { /* 相册 */ }
// 功能越多,这里越臃肿
}
}Activity Result API 中,每个 registerForActivityResult 创建独立的 ActivityResultLauncher,各自有自己的回调,天然隔离。框架内部(ActivityResultRegistry)仍然使用 requestCode 与系统通信,但这个 requestCode 由框架自动分配和管理,开发者完全不需要感知。
优势:解耦(每个 launcher 独立回调,无需集中式 requestCode 分发)、可测试(Contract 可以单独测试)、类型安全(不同 Contract 有明确的输入/输出类型)。
9. PendingIntent
PendingIntent 是对 Intent 的包装,允许其他应用或系统在未来某个时刻代替你执行操作:
// 创建
val pendingIntent = PendingIntent.getActivity(
context, requestCode, intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)使用场景:
- 通知点击(NotificationCompat.Builder.setContentIntent)
- AlarmManager 定时任务
- Widget 点击事件
- 地理围栏触发
FLAG 说明:
FLAG_IMMUTABLE(Android 12+ 必须指定):PendingIntent 不可被修改FLAG_MUTABLE:允许修改(如 inline reply)FLAG_UPDATE_CURRENT:如果已存在则更新 extrasFLAG_ONE_SHOT:只能使用一次
10. 多窗口与画中画
10.1 多窗口模式(Android 7.0+)
<!-- AndroidManifest.xml -->
<activity android:resizeableActivity="true" />生命周期变化:多窗口模式下,可见但非焦点的 Activity 处于 onPause 状态(Android 10+ 改为 onResume,多个 Activity 可以同时 RESUMED)。
10.2 画中画(PiP,Android 8.0+)
// 进入画中画
val params = PictureInPictureParams.Builder()
.setAspectRatio(Rational(16, 9))
.build()
enterPictureInPictureMode(params)
// 监听画中画状态变化
override fun onPictureInPictureModeChanged(isInPiP: Boolean, config: Configuration) {
if (isInPiP) {
// 隐藏非必要 UI 元素
} else {
// 恢复完整 UI
}
}11. Android 各版本重要变更
| 版本 | 重要变更 |
|---|---|
| Android 6.0 (API 23) | 运行时权限 |
| Android 7.0 (API 24) | 多窗口、FileProvider(禁止 file:// URI) |
| Android 8.0 (API 26) | 通知渠道、后台 Service 限制、自适应图标 |
| Android 9.0 (API 28) | 刘海屏适配、禁止 HTTP 明文(需 networkSecurityConfig) |
| Android 10 (API 29) | 分区存储、后台位置权限、深色模式 |
| Android 11 (API 30) | 强制分区存储、包可见性、前台 Service 类型 |
| Android 12 (API 31) | SplashScreen API、PendingIntent 必须指定 mutability、蓝牙权限变更 |
| Android 13 (API 33) | 通知权限(POST_NOTIFICATIONS)、细粒度媒体权限 |
| Android 14 (API 34) | 前台 Service 类型必须指定、照片/视频部分访问权限 |
