第4章 技术面试:Android核心机制深入——Activity、Service、BroadcastReceiver、ContentProvider底层原理与常见陷阱
面试问答卡 1:Activity 启动流程与栈管理
面试问题:请描述 Activity A 启动 Activity B 时,系统从 startActivity 到 onResume 的完整源码流程,涉及哪些关键类?
面试官考察点:对 ActivityManagerService、Instrumentation、ActivityThread 的协作理解;是否清楚栈结构(TaskRecord、ActivityRecord)。
回答框架:1)发起端:startActivity → Instrumentation.execStartActivity → AMS 处理;2)AMS 端:检查权限、创建 ActivityRecord、栈管理、暂停前一个 Activity;3)目标端:ApplicationThread.scheduleLaunchActivity → ActivityThread 通过反射创建 Activity,执行生命周期。
简短示例回答:“调用 startActivity 后,进程通过 Instrumentation 与 AMS 通信。AMS 创建 ActivityRecord 并入栈,调用 pause 当前 Activity。再通过 Binder 通知目标进程,ActivityThread 利用 LoadApk 创建 Activity 实例,顺序调用 onCreate/onStart/onResume。关键类是 ActivityManagerService 和 ActivityThread。” (98字)
可能追问:1)启动模式(singleTop/singleTask)在 AMS 中如何实现?2)onNewIntent 调用时机?3)Activity 销毁重建时状态保存过程?
避坑提醒:不要只背答案,要说出 AMS 与 ActivityThread 是跨进程的;强调 ActivityRecord 是栈管理核心,避免混淆 TaskRecord 与 ActivityRecord。
面试问答卡 2:Service 生命周期与绑定机制
面试问题:Service 的 onCreate、onStartCommand、onBind 调用顺序如何?如果多次 startService 和 bindService,生命周期如何变化?
面试官考察点:Service 两种启动模式差异;是否理解 Service 进程优先级与 ANR 关系;源码中 ServiceRecord 与 ActiveServices 的调度。
回答框架:1)startService:首次 onCreate,然后 onStartCommand,多次 start 只会重复 onStartCommand;2)bindService:如果未启动则先 onCreate,再 onBind;多次 bind 只执行一次 onBind;3)同时混合使用时,需所有 unbind 且 stop 才能销毁。
简短示例回答:“startService 触发 onCreate → onStartCommand;bindService 触发 onCreate → onBind。多次 startService 不会重复 onCreate。若同时存在绑定和启动,需全部 unbind 并且 stopService 才会进入 onDestroy。源码中 ServiceRecord 管理所有连接记录。” (85字)
可能追问:1)onStartCommand 返回值对系统杀进程的影响?2)前台 Service 如何规避低内存被杀?3)onBind 返回的 Binder 对象如何实现的?
避坑提醒:注意 onRebind 只在解绑后再次绑定时触发;不要忽略 Service 在主线程执行长时间操作可能导致 ANR,需要开子线程。
面试问答卡 3:BroadcastReceiver 与 ContentProvider 底层原理
面试问题:全局广播与本地广播在底层实现上有何本质区别?ContentProvider 在多线程场景下如何保证数据一致性?
面试官考察点:是否熟悉 BroadcastRecord、Intent 匹配机制;ContentProvider 的 Binder 代理与 SQLite 锁机制;能否区分线程模型。
回答框架:1)全局广播通过 AMS 的 registedReceivers 列表匹配,跨进程;本地广播使用 Handler 和 LocalBroadcastManager,无需 Binder;2)ContentProvider 默认允许多线程同时调用 query/insert 等,内部需加锁(如 SQLite 的 WAL 模式或自定义同步)。
简短示例回答:“全局广播由 AMS 维护一个 BroadcastRecord 队列,跨进程分发,存在系统广播安全风险。本地广播基于 Handler 发送,仅在当前进程,效率更高且安全。ContentProvider 操作默认多线程并发,建议在 applyBatch 中使用事务,或使用 synced 关键字控制临界区。” (98字)
可能追问:1)有序广播与普通广播在 AMS 调度上的区别?2)动态注册广播与静态注册广播的优先级?3)ContentProvider 的 getType 与 openFile 是如何通过 Uri 映射的?
避坑提醒:不要混淆有序广播与粘性广播;ContentProvider 中避免长时间持有锁导致死锁,可考虑使用 ContentProviderOperation 批量处理。
四大组件常见陷阱对比表
| 组件 | 常见陷阱 | 源码体现 | 推荐处理方式 |
|---|---|---|---|
| Activity | 配置变更导致状态丢失 | ActivityRecord 重建时未调用 onSaveInstanceState | 在 onSaveInstanceState 中保存数据,onRestoreInstanceState 恢复 |
| Service | 绑定服务在宿主进程被杀后泄漏 | ServiceRecord 中 mConnections 引用未被清除 | 在 Activity onDestroy 中调用 unbindService,或使用 Context.BIND_ALIVE |
| BroadcastReceiver | 动态注册后未解除导致内存泄漏 | ContextImpl 中的 mReceiverGuard 链表 | 在 onPause/onStop 中 unregisterReceiver |
| ContentProvider | 跨进程大数据传输导致 Binder 缓冲区溢出 | Binder 传输限制 1MB,ContentProvider 默认使用 CursorWindow | 分页查询,或使用 FileProvider 传递文件路径 |
项目案例:短视频社交 App 消息推送模块
项目名称:短视频社交 App 消息推送模块(Service + BroadcastReceiver 深度集成)
业务背景:用户需要在后台实时接收私信、评论、点赞通知,并能自定义免打扰时段。需要保证进程保活与低功耗。
规模指标:日活 500 万,单日推送量 2 亿条,后台进程存活率要求 > 98%,通知点击率提升 15%。
技术栈:Service(前台 + 后台)、LocalBroadcastManager、JobScheduler、WorkManager、Realm 本地缓存。
核心难点:1)多厂商推送适配导致 Service 被频繁重启;2)大量广播接收导致主线程卡顿;3)消息顺序性和去重(Binder 并发)。
面试高频追问:1)如何设计 Service 的进程优先级以减少被杀概率?2)为什么选择 WorkManager 而非 WakefulBroadcastReceiver?3)如何避免推送广播导致 ANR?
可迁移回答话术:“我们在 Service onStartCommand 中返回 START_STICKY,并结合前台通知位提升优先级。广播接收中使用 HandlerThread 处理耗时逻辑。消息去重采用消息池 + 数据库唯一索引,在 ContentProvider 的 insert 方法中使用事务保证原子性。”