Navigation 框架

- NavHostFragment 是 Android Navigation 组件的页面容器宿主 Fragment,所有 导航图 里定义的 Fragment 都会挂载、渲染在它内部。 可以在 app:navGraph 属性指定路由配置文件,包含了所有的页面信息。
并且NavHostFragment 对象内部 持有了 NavController,所有的页面跳转逻辑都是通过它来分发 - NavController 持有了 NavGraph 以及 NavigatorProvider
- NavGraph 维护了 对应于nav_graph.xml 文件解析得来的节点信息, 内部使用了SparseArray(优化的双列集合)缓存了解析的结果destination, 可以通过 资源 id 来查找 保留了节点信息的 destination 对象。同时记录了 启动页的 资源id。
- NavigatorProvider 则维护了实际的跳转对象 即Navigator,例如ActivityNavigator 和 FragmentNavigator。
最终执行的 NavController.navigate() 会根据 destination 的类型或者说创建destination的navigator的类型,例如Activity、Fragment 得到对应的 navigator 来执行对应的 navigate() 逻辑,最终底层就是 fragment的replace 方法 和 activity 的 startActivity 方法
nav_graph.xml 如何生成路由表
从NavHostFragement→NavGraph
追问:NavGraph 内部是如何存储这些节点的?
NavGraph 内部使用了一个 SparseArrayCompat<NavDestination> (类似哈希表)来存储所有子节点。这个集合以每个 NavDestination 的 id 为键,使得查找操作非常高效。这也是为什么我们在 navigate() 时传入的是 @IdRes 资源 ID
追问:NavigatorProvider 是在哪里初始化的,它怎么知道 navigation 对应 NavGraphNavigator?
NavigatorProvider 的初始化发生在 NavHostController(NavController 的子类)的构造方法中。在初始化时,系统会默认注册 NavGraphNavigator、ActivityNavigator 和 FragmentNavigator 等。每个 Navigator 通过其注解或内部定义来声明它负责处理的 XML 标签名,例如 NavGraphNavigator 对应的就是 navigation 标签
startDestinationId 启动页是如何被加载的
旧栈会被清空。 在 setGraph() 的开头,源码会主动调用 popBackStackInternal(null, true),把所有旧的回退栈条目全部清除。这意味着新设置的 startDestination 一定是新栈底的唯一页面,用户按返回键不会回到旧图里的页面。 即 先清空栈才会调用
NavController.navigate()
NavController.navigate() 简化逻辑
与启动页加载的方式是几乎一致的
简化源码:
// NavController.java (源码简化)
private void navigate(@IdRes int resId, Bundle args, NavOptions options, Navigator.Extras extras) {
// 1. 从当前 NavGraph 的 SparseArray 中查找到目标节点
NavDestination destination = mGraph.findNode(resId, false);
// ⭐ 2. 关键:通过目标节点获取导航器名称 (例如 "fragment")
String navigatorName = destination.getNavigatorName();
// ⭐ 3. 关键:通过 NavigatorProvider 根据名称取到真正的导航器实例
Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(navigatorName);
// 4. 调用导航器的 navigate 方法执行跳转
navigator.navigate(destination, args, options, extras);
}
跳转到 navigation 页面
对于 navigate 到一个 Activity 最后会 调用 ActivityNavigator.navigate(), 然后根据转入的 destination 调用 ActivityCompat.startActivity() 来启动目标 Activity
缺点
- 需要在 nav_graph.xml 提前配置所有页面节点,不易维护. 只有当时写得人容易看懂,而后续维护的人会很费时间。 而且页面多起来时,会导致文件过于臃肿
- Fragment 类型的路由导航使用的replace,会使得页面频繁重建,体验不够友好
- Fragment singleTop启动模式,即便目标页在栈顶,依然会销毁后再重建,体验不友好
1285

被折叠的 条评论
为什么被折叠?



