Skip to content

四大组件 / 生命周期 / 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 → onDestroy
  • onCreate:创建 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 → onResume

Activity 被销毁重建。数据保存在 onSaveInstanceState(Bundle),恢复在 onCreateonRestoreInstanceState

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 的查找逻辑:

  1. 遍历所有 TaskRecord,查找是否有匹配的 ActivityRecord
  2. 找到则将该 Task 移到前台,清除目标 Activity 上方的所有 Activity
  3. 调用目标 Activity 的 onNewIntent

2.3 Intent Flags

java
// 等同于 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 已创建,可以安全地 findViewById
  • onDestroyView:View 被销毁但 Fragment 实例可能还在(如加入 back stack)

3.2 ViewLifecycleOwner

Fragment 有两个生命周期:

  • Fragment 自身生命周期(onCreate → onDestroy)
  • View 生命周期(onCreateView → onDestroyView)

重要:在 Fragment 中观察 LiveData/Flow 时,应该使用 viewLifecycleOwner 而非 this

kotlin
// ✅ 正确:使用 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 通信

kotlin
// 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 为 null
    • START_REDELIVER_INTENT:重启并重新传递最后一个 Intent

bindService

onCreate → onBind → [客户端绑定中] → onUnbind → onDestroy
  • 返回 IBinder 供客户端通信
  • 所有客户端解绑后自动销毁
  • 可以同时 start + bind,需要同时 stop + unbind 才会销毁

4.2 前台 Service

Android 8.0+ 后台 Service 限制严格,长时间运行需要前台 Service:

kotlin
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+ 大部分隐式广播不再支持静态注册)
  • 适合:开机广播、应用安装/卸载广播

动态注册(代码):

kotlin
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
│       └── Activity

7.2 不同 Context 的能力差异

操作ApplicationActivityService
启动 Activity✅(需 NEW_TASK)✅(需 NEW_TASK)
弹 Dialog
Layout Inflation⚠️(无主题)⚠️(无主题)
启动 Service
发送广播
访问资源

核心原则

  • 涉及 UI 的操作用 Activity Context
  • 生命周期长的操作(如单例持有)用 Application Context,避免内存泄漏
  • getApplicationContext() 返回 Application 实例
  • getBaseContext() 返回 ContextWrapper 内部的 ContextImpl

7.3 常见坑

kotlin
// ❌ 内存泄漏:单例持有 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(已废弃):

kotlin
// 注册(在 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 区分:

kotlin
// 旧方式:所有结果集中在一个回调,用 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 的包装,允许其他应用或系统在未来某个时刻代替你执行操作:

kotlin
// 创建
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:如果已存在则更新 extras
  • FLAG_ONE_SHOT:只能使用一次

10. 多窗口与画中画

10.1 多窗口模式(Android 7.0+)

xml
<!-- AndroidManifest.xml -->
<activity android:resizeableActivity="true" />

生命周期变化:多窗口模式下,可见但非焦点的 Activity 处于 onPause 状态(Android 10+ 改为 onResume,多个 Activity 可以同时 RESUMED)。

10.2 画中画(PiP,Android 8.0+)

kotlin
// 进入画中画
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 类型必须指定、照片/视频部分访问权限