四大组件 / 生命周期 / Context — 面试题篇
更新: 5/15/2026 字数: 0 字 时长: 0 分钟
Q1: Activity 的生命周期?A 启动 B 时生命周期调用顺序?
考察点:生命周期理解深度
完整回答:
正常生命周期:onCreate → onStart → onResume → onPause → onStop → onDestroy。
A 启动 B:A.onPause → B.onCreate → B.onStart → B.onResume → A.onStop
B 返回 A:B.onPause → A.onRestart → A.onStart → A.onResume → B.onStop → B.onDestroy
关键点:A.onPause 先于 B.onCreate 执行,所以 onPause 中不能做耗时操作,否则会延迟 B 的显示。
追问:如果 B 是透明 Activity 或 Dialog 主题?
A 不会调用 onStop(因为 A 仍然部分可见),只调用 onPause。B 关闭后 A 直接调用 onResume。
追问:配置变更(旋转屏幕)时的生命周期?
onPause → onStop → onSaveInstanceState → onDestroy → onCreate(bundle) → onStart → onRestoreInstanceState → onResume
Activity 被销毁重建。可以通过 android:configChanges="orientation|screenSize" 阻止重建,此时只回调 onConfigurationChanged。
加分点:提到 ViewModel 通过 onRetainNonConfigurationInstance 在配置变更时保留,不受销毁重建影响。
Q2: Activity 的四种启动模式?
考察点:Task 和启动模式
完整回答:
- standard:默认模式,每次启动创建新实例
- singleTop:栈顶复用。已在栈顶则调用 onNewIntent,不在栈顶则新建
- singleTask:栈内复用。在目标 Task 中查找,存在则 clearTop 并调用 onNewIntent
- singleInstance:独占一个 Task,系统中只有一个实例
使用场景:
- singleTop:通知点击打开的页面(避免重复创建)、搜索页面
- singleTask:App 首页(从任何地方回到首页清除中间页面)
- singleInstance:来电界面、系统级页面
追问:singleTask 一定会在新 Task 中吗?
不一定。singleTask 首先查找 taskAffinity 匹配的 Task,如果已存在则在该 Task 中复用。默认 taskAffinity 是包名,所以默认情况下 singleTask 的 Activity 会在应用的主 Task 中。只有指定了不同的 taskAffinity 才会创建新 Task。
追问:FLAG_ACTIVITY_CLEAR_TOP 和 singleTask 的区别?
CLEAR_TOP 只是清除目标 Activity 上方的 Activity。如果目标是 standard 模式,会先销毁再重建(不调用 onNewIntent)。singleTask 则是复用已有实例(调用 onNewIntent)。CLEAR_TOP + SINGLE_TOP 的组合效果等同于 singleTask。
Q3: Fragment 的生命周期?为什么要用 viewLifecycleOwner?
考察点:Fragment 双生命周期
完整回答:
Fragment 生命周期:onAttach → onCreate → onCreateView → onViewCreated → onStart → onResume → onPause → onStop → onDestroyView → onDestroy → onDetach。
Fragment 有两个生命周期:
- Fragment 实例生命周期(onCreate → onDestroy)
- View 生命周期(onCreateView → onDestroyView)
当 Fragment 加入 back stack 后按返回,View 被销毁(onDestroyView)但 Fragment 实例还在。再次显示时重新走 onCreateView。
如果在 Fragment 中用 this 作为 LifecycleOwner 观察 LiveData,Fragment 返回 back stack 时 Observer 不会被移除(Fragment 没有 destroy)。再次显示时又注册新的 Observer,导致重复观察。
用 viewLifecycleOwner 则 Observer 跟随 View 生命周期,onDestroyView 时自动移除。
追问:Fragment 之间怎么通信?
推荐方案:
- 共享 ViewModel:
by activityViewModels()获取 Activity 级别的 ViewModel - Fragment Result API:
setFragmentResult/setFragmentResultListener - Navigation 的 SavedStateHandle
不推荐:直接引用其他 Fragment、接口回调(耦合度高)。
Q4: Service 的两种启动方式?区别是什么?
考察点:Service 生命周期
完整回答:
startService:
- 生命周期:onCreate → onStartCommand → 运行 → onDestroy
- 调用者与 Service 无绑定关系,调用者销毁 Service 继续运行
- 需要主动调用 stopSelf() 或 stopService() 停止
- onStartCommand 返回值决定被杀后重启策略
bindService:
- 生命周期:onCreate → onBind → 运行 → onUnbind → onDestroy
- 返回 IBinder 供客户端通信
- 所有客户端解绑后自动销毁
- 适合需要与 Service 交互的场景
可以同时 start + bind,此时需要同时 stop + unbind 才会销毁。
追问:Android 8.0+ 对后台 Service 有什么限制?
应用进入后台后约 1 分钟,系统会停止后台 Service。解决方案:
- 前台 Service:
startForeground()显示通知 - WorkManager:适合可延迟的后台任务
- JobScheduler:系统调度的任务
Android 12+ 前台 Service 需要声明 foregroundServiceType,Android 14+ 必须指定具体类型。
Q5: ContentProvider 的启动时序?为什么很多库用它做初始化?
考察点:ContentProvider 原理
完整回答:
启动时序:Application.attachBaseContext → ContentProvider.onCreate → Application.onCreate
ContentProvider 在 Application.onCreate 之前初始化,且系统会自动实例化 Manifest 中声明的所有 ContentProvider。很多库利用这个特性做自动初始化(如 Firebase、LeakCanary、WorkManager),用户不需要手动在 Application 中调用 init 方法。
缺点:每个 ContentProvider 都会拖慢启动速度(约 2-5ms)。如果多个库都用这种方式,累积影响明显。
Jetpack App Startup 库的解决方案:提供一个统一的 ContentProvider(InitializationProvider),所有库的初始化逻辑注册为 Initializer,合并在一个 ContentProvider 中执行,减少开销。
追问:ContentProvider 的跨进程通信原理?
底层通过 Binder。客户端通过 ContentResolver 发起请求,AMS 查找目标 Provider 的 Binder 引用并返回给客户端。后续客户端直接通过 Binder 与 Provider 通信。大数据传输通过 ParcelFileDescriptor 传递文件描述符,绕过 Binder 1MB 大小限制。
Q6: Context 的继承体系?Application Context 和 Activity Context 有什么区别?
考察点:Context 体系
完整回答:
继承关系:
Context(抽象类)
├── ContextImpl(真正实现)
└── ContextWrapper(装饰器)
├── Application
├── Service
└── ContextThemeWrapper
└── Activity区别:
- Activity Context 包含主题信息(继承 ContextThemeWrapper),可以启动 Activity、弹 Dialog
- Application/Service Context 没有主题,不能直接启动 Activity(需要 FLAG_ACTIVITY_NEW_TASK),不能弹 Dialog
使用原则:
- UI 相关(Dialog、Toast、inflate 布局):用 Activity Context
- 生命周期长的对象(单例、全局缓存):用 Application Context,避免持有 Activity 导致内存泄漏
追问:为什么不能用 Application Context 弹 Dialog?
Dialog 需要依附于一个 Window,而 Window 需要 WindowManager.LayoutParams 中的 token。Activity 有自己的 Window 和 token,Application Context 没有。用 Application Context 弹 Dialog 会抛 BadTokenException。
加分点:提到 getApplicationContext() 返回的是 Application 对象,getBaseContext() 返回的是 ContextImpl。ContextWrapper 的所有方法都委托给 mBase(ContextImpl)。
Q7: onSaveInstanceState 和 ViewModel 的区别?什么时候用哪个?
考察点:状态保存机制
完整回答:
| 维度 | onSaveInstanceState | ViewModel |
|---|---|---|
| 存储位置 | Bundle(序列化到磁盘) | 内存 |
| 大小限制 | ~1MB(Bundle 限制) | 无限制(受堆内存限制) |
| 配置变更 | ✅ 保留 | ✅ 保留 |
| 进程被杀 | ✅ 保留 | ❌ 丢失 |
| 数据类型 | 可序列化的简单数据 | 任意对象 |
选择:
- 轻量关键数据(搜索关键词、列表位置、页码)→ onSaveInstanceState 或 SavedStateHandle
- 大量数据(列表数据、网络响应)→ ViewModel
- 需要进程恢复的关键数据 → SavedStateHandle(ViewModel 中使用 Bundle)
追问:onSaveInstanceState 什么时候调用?
Android 9(API 28)之前:在 onStop 之前调用。 Android 9+ 之后:在 onStop 之后调用。 只在系统可能销毁 Activity 时调用(如旋转屏幕、按 Home 键、切换 App),用户主动按返回键不会调用。
Q8: Activity 的 taskAffinity 是什么?怎么影响启动模式?
考察点:Task 管理
完整回答:
taskAffinity 是 Activity 的"亲和力"属性,决定 Activity 倾向于属于哪个 Task。默认值是应用包名。
影响:
- singleTask:先查找 taskAffinity 匹配的 Task,找到则在该 Task 中复用/创建 Activity。不同 taskAffinity 会创建新 Task。
- FLAG_ACTIVITY_NEW_TASK:如果目标 Activity 的 taskAffinity 与当前 Task 不同,会在匹配的 Task 中启动(或创建新 Task)。
- allowTaskReparenting:当 Activity 的 taskAffinity 对应的 Task 来到前台时,Activity 会从当前 Task 移到该 Task。
<activity
android:name=".DetailActivity"
android:taskAffinity="com.example.detail"
android:launchMode="singleTask" />Q9: Android 的权限机制?运行时权限怎么处理?
考察点:权限系统
完整回答:
Android 6.0+ 引入运行时权限,危险权限需要在运行时请求:
// 推荐:Activity Result API
val launcher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val cameraGranted = permissions[Manifest.permission.CAMERA] ?: false
val locationGranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] ?: false
if (cameraGranted && locationGranted) {
// 权限已授予
} else {
// 处理拒绝
if (!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
// 用户选择了"不再询问",引导去设置页
}
}
}
// 请求
launcher.launch(arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.ACCESS_FINE_LOCATION
))权限最佳实践:
- 最小权限原则:只请求必要的权限
- 在使用功能时才请求(不要启动时一次性请求所有权限)
- 被拒绝后解释为什么需要(shouldShowRequestPermissionRationale)
- 用户选择"不再询问"后引导去设置页
Q10: IntentFilter 的匹配规则?隐式启动 Activity 的过程?
考察点:Intent 解析
完整回答:
隐式 Intent 需要同时匹配 action、category 和 data 三个条件:
- action:Intent 的 action 必须与 IntentFilter 中的某一个 action 匹配
- category:Intent 的所有 category 都必须在 IntentFilter 中存在。系统会自动添加
CATEGORY_DEFAULT,所以 IntentFilter 必须包含它 - data:匹配 URI(scheme://host:port/path)和 mimeType
<intent-filter>
<action android:name="com.example.SHOW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https" android:host="example.com" />
</intent-filter>追问:startActivity 找不到匹配的 Activity 会怎样?
抛 ActivityNotFoundException。安全做法是先用 intent.resolveActivity(packageManager) 检查,或用 try-catch 包裹。
Android 11+ 包可见性限制:需要在 Manifest 中声明 <queries> 才能查询其他应用的 Activity。
实习面试补充:四大组件基础高频题
实习面试更看重组件“会不会用、生命周期会不会踩坑”,不一定要求一开始就讲 AMS 源码。
Q11: Intent 显式启动和隐式启动有什么区别?
考察点:Intent 基础
完整回答:
- 显式 Intent:明确指定目标组件,通常用于应用内部页面跳转。
- 隐式 Intent:不指定具体组件,而是通过 action、category、data 让系统匹配能处理的组件,常用于打开浏览器、相机、分享等跨应用场景。
// 显式启动
startActivity(Intent(this, DetailActivity::class.java))
// 隐式启动
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.example.com"))
startActivity(intent)追问:隐式启动有什么风险?
可能没有应用能处理,导致 ActivityNotFoundException。启动前可以使用 resolveActivity(packageManager) 判断,或用 try-catch 兜底。
Q12: Activity 和 Fragment 分别适合承担什么职责?
考察点:页面组织、职责划分
完整回答:
Activity 通常作为页面容器,负责承载 Fragment、处理系统入口、权限、导航和生命周期协调。Fragment 更适合作为可复用的 UI 模块,负责具体页面内容和交互。
常见实践是单 Activity + 多 Fragment,配合 Navigation 管理页面跳转;也可以多 Activity,但不要把所有业务逻辑都堆在 Activity 中。
加分点:Fragment 有 View 生命周期,访问 ViewBinding 时要在 onDestroyView 置空,观察 LiveData/Flow 时优先绑定 viewLifecycleOwner。
Q13: Service 是不是运行在子线程?
考察点:Service 基础误区
完整回答:
不是。普通 Service 默认运行在主线程,只是没有界面,并不代表自动在后台线程执行。如果在 Service 中做耗时任务,仍然需要自己切到子线程,否则也可能造成 ANR。
如果是需要长期运行且用户可感知的任务,应使用前台服务并展示通知;如果是可延迟的后台任务,可以考虑 WorkManager。
追问:IntentService 现在还推荐吗?
IntentService 已废弃。现在更推荐根据场景使用 WorkManager、协程、线程池或前台服务。
