3. 导航栈核心组件与职责
3.1 四栈总体边界与分层
Android 导航体系中存在四个相互关联但又职责隔离的栈结构:Activity 栈、Fragment 栈、Task 栈和返回栈。它们分别管理不同粒度的界面单元与用户操作历史。下面表格定义每个栈的管理单元、生命周期锚点以及系统层归属。
| 栈名称 | 管理单元 | 生命周期锚点 | 系统层级 |
|---|---|---|---|
| Activity 栈(ActivityRecord 栈) | ActivityRecord(单个 Activity 实例) | onCreate → onStart → onResume → onPause → onStop → onDestroy | SystemServer / ActivityManagerService |
| Fragment 栈(BackStackRecord) | Fragment 实例 | onAttach → onCreateView → onStart → onResume → onPause → onStop → onDestroyView → onDetach | App 进程 / FragmentManager |
| Task 栈(TaskRecord 栈) | TaskRecord(一组 Activity 的集合) | Task 被创建/移至前台/被覆盖/销毁(对应所有 Activity 生命周期) | SystemServer / ActivityTaskManagerService |
| 返回栈(ActivityStack 中的 mRecentTasks) | TaskRecord 或 ActivityRecord(取决于启动模式) | 按最后操作时间排序,清除策略由系统决定 | SystemServer / SystemUI(最近任务) |
边界总结:Activity 栈管理「可见 Activity」的实例堆叠;Fragment 栈管理「Fragment 事务」的逆向回退;Task 栈管理「任务容器」的层级与前台转移;返回栈管理「用户感知的历史 Tasks」。四者共同构成完整的导航栈体系,任何一层的状态不一致都会导致界面跳转异常或内存泄漏。
3.2 组件职责与信任链路
3.2.1 Activity 栈 —— 可见界面核心
Activity 栈由 ActivityManagerService 维护,以 ActivityRecord 为节点。每个 ActivityRecord 持有 ActivityInfo、TaskRecord 引用以及进程标识。其核心职责:
- 维护前台 Activity 的焦点切换(
resumedActivity)。 - 处理启动模式(standard / singleTop / singleTask / singleInstance)对栈的操作。
- 控制 Activity 生命周期转运(Pause → Stop → Destroy 等)。
- 提供
ActivityStackSupervisor对多 Display / 多栈协同管理。
3.2.2 Fragment 栈 —— 动态 UI 片断管理
Fragment 栈由 FragmentManager 内部的 BackStackRecord 链表实现。每个事务(FragmentTransaction)会生成一个 BackStackRecord,记录增删改操作。信任链路要求:
- Fragment 栈的 pop 操作必须完全对应 push 时的逆操作,否则造成视图混乱。
- Fragment 栈不能直接操作 Activity 栈,必须通过
FragmentManager向宿主 Activity 发送回调。
3.2.3 Task 栈 —— 任务隔离与复用
TaskRecord 包含一个 ArrayList<ActivityRecord>,同时持有 mAffinity(任务亲和性)、mIntent、mRoot 等属性。Task 栈的信任边界:
- 不同 Task 之间 Activity 不能直接互相访问(跨 Task 需通过
FLAG_ACTIVITY_NEW_TASK等)。 - 系统通过
isLockTaskMode策略限制 Task 栈的修改。
3.2.4 返回栈 —— 用户心智模型映射
返回栈在 SystemUI 层以 RecentTasks 呈现,由 ActivityTaskManagerService 按最近使用时间排序。它并不直接管理 Activity 实例,而是管理 TaskSnapshot 快照。信任链路要求 SystemUI 通过 ITaskStackListener 监听栈变化。
常见风险点:若 Fragment 栈和 Activity 栈的回退操作不同步(例如用户按 Back 键时,Activity 栈 pop 而 Fragment 栈未清理),会导致 Fragment 泄漏或状态残留。解决方案是统一使用 Navigation Component 或自行封装导航控制器,将返回逻辑收敛至单一入口。
3.3 数据流与控制流
下图展示典型交互场景(按 BACK 键)中各栈间的控制流与数据流。
3.4 选型权衡与落地步骤
3.4.1 是否需要独立管理 Fragment 栈?
- 轻量屏内导航:可委托给系统 FragmentManager,无需自行包装。
- 深度嵌套 + 复杂动画:建议在 Activity 栈基础上叠加自己的导航器,保留
BackStackRecord的控制权。 - 跨 Task 场景:不要依赖 Fragment 栈保持跨 Task 状态,应使用
onSaveInstanceState持久化。
3.4.2 落地步骤(7 步)
- 定义自己的
RouteNode数据结构,包含 intent/fragmentClass/栈类型标识。 - 创建
NavigationController封装startActivity和FragmentTransaction,禁止外部直接调用。 - 在
Activity.onBackPressed中优先委托 NavigationController 处理。 - 注册
ActivityManagerInternal的 Task 栈监听(SystemUI 需要)。 - 在 Task 切换或返回栈变化时,同步更新 UI 中的返回键状态及历史快照。
- 压力测试:频繁压榨 Back 键,验证 Activity/Fragment 栈无泄漏。
- 集成三方库时(如 Jetpack Navigation),确保其内部栈与自定义控制器不冲突。
验收标准(建议包含在课程练习中):
- 在任意深度下连续按 Back 键,所有 Fragment 被正确回退,且 Activity 栈保持不变。
- 从最近任务列表切换另一个 Task 后返回,原 Task 的 Activity 栈和 Fragment 栈恢复正确。
- 通过
dumpsys activity activities检查每个栈中 Record 数量符合预期。 - SystemUI 状态栏返回键显示/隐藏逻辑与实际栈深度一致。