Navigation在SystemUI中的集成实践 - 第1节
1.1 SystemUI 进程与 APK 结构
SystemUI 是 Android 系统中运行在独立进程 (com.android.systemui) 的系统级应用,其 APK 位于 /system_ext/priv-app/SystemUI/SystemUI.apk。Navigation 组件作为用户与系统手势、物理按键交互的核心通道,深度嵌入 SystemUI 的进程模型与视图层级中。
SystemUI 的关键模块依赖关系如下表:
| 模块/服务 | 进程 | 与 Navigation 的耦合方式 |
|---|---|---|
| SystemUIApplication | systemui | Application 入口,负责初始化 Navigation 的 Fragment 与 ViewModel |
| StatusBar | systemui | 通过 shared ViewModel 读取导航栏可见性与沉浸模式状态 |
| NavigationBarController | systemui | 直接持有 NavigationBarFragment,处理手势/按键事件路由 |
| QuickSettingsPanel | systemui | 通过全局 Broadcast 互斥控制展开/收起 |
| SystemUIService | systemui | 绑定 SystemUI 的生命周期,Navigation 作为内部组件注册 |
1.2 Navigation 在 SystemUI 中的角色定位
Navigation 在 SystemUI 中不是单一的 View,而是一组协同工作的组件集合,它们共同支撑用户从任意界面快速返回桌面、切换最近任务、触发系统级操作的能力。核心组件职责如下:
-
NavigationBarView
物理/虚拟导航栏的 View 层容器,负责渲染返回、Home、最近任务三个按钮或手势指示器。 -
NavigationBarFragment
持有一个 NavHostFragment,管理导航栏内部的 Fragment 切换(如 2 按钮 / 3 按钮模式切换)。 -
NavHostFragment
androidx.navigation提供的宿主,承载 Navigation 图 (nav_graph) 中定义的 destination。 -
GestureController
处理从底部边缘滑动、侧滑返回等手势的识别与事件分发,最终转化为 Navigation 的 Action。
角色总结: Navigation 是 SystemUI 中连接 用户输入 → 窗口策略 → ActivityManagerService 的调度中枢,也是手势/按键交互向系统级意图转换的最后关口。
1.3 集成入口与启动流程
Navigation 组件的初始化入口位于 SystemUIApplication.onCreate() 中。启动链路由以下步骤组成:
- SystemUIApplication 初始化
SystemUIService并绑定到系统进程。 SystemUIService.onCreate()中调用NavigationBarController.create()。NavigationBarController加载布局navigation_bar_view,实例化NavigationBarFragment。NavigationBarFragment.onCreateView()中通过NavHostFragment.create(R.navigation.nav_bar_navigation)创建宿主。NavHostFragment根据nav_bar_navigation.xml导航图加载初始 destination(通常是NavBarButtonsFragment或GestureHintFragment)。
关键集成的代码摘录如下:
// NavigationBarController.java
public void create(Context context, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View root = inflater.inflate(R.layout.navigation_bar_view, parent, false);
parent.addView(root);
NavigationBarFragment fragment = new NavigationBarFragment();
getFragmentManager().beginTransaction()
.replace(R.id.nav_host_container, fragment)
.commit();
}
// NavigationBarFragment.java
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 注意:此处使用 NavHostFragment.create() 而非直接 inflate
NavHostFragment navHost = NavHostFragment.create(R.navigation.nav_bar_navigation);
return navHost.requireView();
}
1.4 导航图配置与路由表
SystemUI 中 Navigation 的路由表以 XML 导航图定义,置于 res/navigation/nav_bar_navigation.xml 中。以下是一个最小示例:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_bar_navigation"
app:startDestination="@id/navBarButtonsFragment">
<fragment
android:id="@+id/navBarButtonsFragment"
android:name="com.android.systemui.navigationbar.NavBarButtonsFragment"
android:label="导航栏按钮"
tools:layout="@layout/nav_bar_buttons" />
<fragment
android:id="@+id/gestureHintFragment"
android:name="com.android.systemui.navigationbar.GestureHintFragment"
android:label="手势提示"
tools:layout="@layout/gesture_hint" />
<action
android:id="@+id/action_to_gestureHint"
app:destination="@id/gestureHintFragment" />
</navigation>
路由表的加载发生在 NavHostFragment 创建时,通过 NavController 的 setGraph() 方法完成。运行时切换 destination 的标准调用方式如下:
// 从 NavBarButtonsFragment 切换到 GestureHintFragment
NavController navController = NavHostFragment.findNavController(this);
navController.navigate(R.id.action_to_gestureHint);
1.5 常见集成错误与避坑
常见错误 1: 在 NavigationBarFragment 中直接 inflate 布局而非使用 NavHostFragment.create()。
后果: 不会触发 Navigation 图加载,导航按钮无法响应路由。
解决: 必须返回 navHost.requireView(),且 NavHostFragment 的布局必须包含 NavHost (通常为 FragmentContainerView)。
调试技巧: 使用 adb shell dumpsys activity container | grep NavigationBar 检查 NavigationFragment 是否绑定正确;或开启 Navigation 的 verbose 日志:
adb shell setprop log.tag.navfragment VERBOSE
adb logcat -s NavFragment
1.6 架构模块边界与数据流(SVG 图)
SVG 图展示了 SystemUI 中 Navigation 组件与周边模块的边界关系和核心数据流,图中使用本标准配色:
SVG 图中清晰展示:
- 系统服务层(蓝):AMS、WMS、IMS 通过 Binder 向 SystemUI 进程传递窗口策略、按键事件、手势原始数据。
- Navigation 核心组件(红/橙):NavigationBarView 与 GestureController 接收并处理这些输入,转换成 Navigation Action。
- 状态共享(绿/青):StatusBar 与 QuickSettings 通过共享 ViewModel / 全局广播与 Navigation 模块交换沉浸模式、控制中心展开状态。
1.7 性能与稳定性优化基准
在正式集成前,需要确认以下性能基线,后续章节将逐条展开优化方法:
| 指标项 | 目标值 | 测试方法 |
|---|---|---|
| NavigationBarFragment 首次创建耗时 | < 15ms | FrameMetrics 或 Systrace |
| 从触摸到 Navigation Action 执行延迟 | < 50ms | InputStage 日志时间戳差值 |
| Navigation 图加载完成时间 | < 5ms | NavController.setGraph() 前后插桩 |
| 手势冲突导致 ANR 次数 | 0/1000 次测试 | Monkey 测试 + 手势场景注入 |