【Android】浅谈Navigation

一,概述

androidx.navigation封装了FragmentManager CRUD操作,简化了单Actively多Fragment架构,本文着重解析Navigation设计思路,底层实现

二,原理

1,inflate

FragmentContainerView设置name参数为NavHostFragment,并且传入navGraph参数,在NavHostFragment#onInflate回调中,便会解析navGraph,如下。

inflate阶段主要保存所有声明的导航判断,其声明内容在哦res/navigation/目录下的xml,如下

大致定义了fragment标签、action标签、以及arguments和include。include可以包含其他导航判断,这个在navigate详细讲解。

回到Inflate解析阶段,做了两件事,如下

保存graphId和mDefualtNavHost参数,在onCreate开始解析,跟进到onCreate

onCreate中初始化NavHostController,并且设置Graph到controller中,

NavHostController中依赖了Navigator接口,Navigator即导航者抽象类,如下

其关键抽象方法定义如下

即navigate和popBackStack。具体实现主要是如下类

在此读者简单解释下几个实现类,

FragmentNavigator:负责Fragment的导航

ActivityNavigator:负责Activity跳转导航

NavGraphNavigator:负责include片段定义的Graph,即跳转到其他Graph的导航

在NavController中,预先add了两个Navigator,NavGraphNavigator和ActivityNavigatror

在onCreateView回调中,再次添加了DIalogFragmentNavigator和FragmentNavigator,

回到正题,跟进解析Graph逻辑

NavInflater#inflate方法解析了graph xml文件,生成一个NavGraph对象,NavGraph是NavDestination子类。笔者在此简单解释下NavDestination。

一个Navdestination存储了导航Id,Name和label,即具体的导航信息

NavGraph和NavDestination关系如同View和ViewGroup,NavGraph包含了多个NavDestination,存储在mNodes成员中,

跟进NavInflater#inflate方法

1,如上,拿到element name,获取Navigator,调用onCreateDestination方法创建一个Navdestionation,并且回调onInflate方法绑定参数。

以FragmentNavigator为例,createDestionation方法实现如下,简单将FragmentNavigatir传入Navdestionation,并且绑定具体Fragment的ClassName

2,解析include、argument和action标签,setGraph由于是root节点解析,会从NavGraphNavigaotr创建一个NavDestionation,并绑定startDestId参数,如下,

随后,将虽有的Destionation添加到NavGraph,包括fragment片段和action等

3,action的解析

拿到launchSingTop、转场动画、popUpTo、argument标签等,存到NavAction中。然后NavAction被保存到NavDestionation中,类似activity tag和action tag的关系。

因此,setGraph解析了所有xml中的导航意图(NavDestionation),这个NavGraph被NavController持有,继续跟进下startId的显示。

2,startId

这里谈下staretId的展示

DefaultNavHost为true,NavFragmentHost会被设置为PrimaryNavigationFragment,作为导航Fragment,back优先回调其popBackStack方法,

在NavController的setGraph中,inflate解析的NavGraph存储到mGraph成员,并且回调onCraphCreated回调

onCraphCreated最后一段,会调用navigate方法,如下

mGraoh即NavGraph,navigate这个NavGraph,会调用NavGraphNavitor#navigate方法,

拿到startDestination,如果没有则抛出异常。因此,setGraph就会navigate到startid,如果存在的话。

3,navigate

接2讲起,startId拿到navigator,这里以fragment为例。

1,反射newInstance一个Fragment,通过setArgument设置navigate的参数

2,ft中绑定自定义动效

3,处理isSingleTopReplacement逻辑,popBackStack稍后讲解。

最后通过ft#commit,再将destionation存到FragmentNavigor的mBackStack中,返回NavDestionation,

4,navigator返回了navDestionation,NavController再进行Stack处理,将navDestionation再次封装为NavBackStackEntry实例,放到NavController#mBackStack中

NavBackStackEntry可以理解为具体的Fragment实例抽象,其实现了LifeCyclerOwner接口,

NavController监听了LifeCycler,在onStateChanged中,进一步调用NavBackStackEntry#handleLifecycleEvent方法,

LifeCyclerOwner是NavFragmentHost,onCreate传入,因此NavFragmentHost的LifeCycler变化会同步到所有的BackStackEntry#LifeCycler中

但是navigate的lifeCycle变化,则是在dispatchOndestinationChanged回调中由NavController控制,

dispatchOndestinationChanged与navigate和pop操作挂钩

4,popBackStack

NavController#popBackStack方法如下

跟进

popped为true,则会触发displatchOnDestionationChanged方法,执行lifeCycle

跟进popBackStackInternal

1,根据pop的destinationId,拿到Navigator,放到popOperations容器。popBackStack的destinationId就是currentId,inclusive参数为true

2,找到了destinationId,break

3,如果没有找到destinationId,打印日志返回

4,调用navigator的popBackStack方法,有多少给id被pop,则调用多少次。即popOperations容器会放置多个重复的Navigator实例,代表需要顺序pop多少次。并且此处会触发NavBackStackEntry#Destroy回调

跟进Navigtor的popBackStack,此处还是以FragmentNavigtor为例

调用fm#popBackStack方法,并且移除mBackStack最后一个。

最后,调用displatchOnDestionationChanged方法,将mBackStack所有Entry走到合适周期,

相关推荐
独隅9 小时前
Claude API 实战教程:从入门到完整项目
android·java·数据库·claude·claude code
且随疾风前行.13 小时前
Paint Cavans高级绘制:[一]Paint画笔高级应用
android·ui
郑州光合科技余经理15 小时前
同城小程序开发:同一笔单在用户端和商家端各显示到哪
android·java·开发语言·前端·后端·uni-app·php
pigdreams16 小时前
Google 官方 Android CLI:让AI直接读布局结构而不是用adb截图验证
android·人工智能·adb
hunterandroid18 小时前
[Android 从零到一] Glide 图片加载原理:三级缓存、生命周期绑定与内存优化
android
法的空间19 小时前
AI 编程时代,为什么 Dart 分析会拖慢大型项目的开发
android·flutter·ios
鱼儿也有烦恼19 小时前
客户端应用架构模式
android·架构
独隅19 小时前
Claude 高效应用实战指南
android·java·开发语言·claude·claude code
硅基手札20 小时前
Android 系统全景专栏 00 · 总目录
android·操作系统·嵌入式开发
YB137520 小时前
Jetpack Compose 动画Crossfade
android