1、Navigation组件初探:什么是Navigation?为什么需要单Activity架构?Navigation组件解决了什么问题?
各位同学,咱们今天正式开讲Navigation组件。说实话,这个组件刚出来的时候,我第一反应是:Google又搞了个新轮子?但真正用上之后,嗯,真香。
先聊聊我个人的经历。几年前我接手一个老项目,那代码里Activity多得吓人,光登录流程就拆了三个Activity。每次跳转都要写Intent,传参靠Bundle,返回结果靠onActivityResult。维护起来?别提了,改个流程能改到怀疑人生。
后来Google推出了Navigation组件,我试着用了一下。怎么说呢,就像从手动挡换成了自动挡。你不需要再操心离合和换挡时机,只管踩油门就行。
什么是Navigation?
Navigation是Android Jetpack家族的一员。它的核心作用就一句话:帮你管理应用内的页面跳转。
你想想看,以前我们怎么跳转页面?写Intent,写Bundle,处理返回栈,处理Fragment事务。代码又臭又长,还容易出bug。
Navigation组件把这些都封装好了。你只需要在XML里画一张导航图,告诉它页面A能跳到页面B,页面B能跳到页面C。剩下的,它全帮你搞定。
核心概念:
- NavHost:导航容器,负责显示目标页面
- NavController:导航控制器,负责执行跳转
- NavGraph:导航图,定义页面之间的跳转关系
说白了,Navigation就是给Fragment跳转加了个导航系统。就像你开车用导航一样,告诉它起点和终点,它帮你规划路线。
为什么需要单Activity架构?
这个问题我经常被问到。很多新手觉得:多Activity也挺好啊,一个页面一个Activity,多清晰。
嗯,表面上看确实清晰。但实际项目中,问题就来了。
我记得有个项目,用户从首页点进商品详情,然后去支付,支付完跳回订单列表。如果每个页面都是Activity,返回栈管理就变成噩梦。用户按返回键,到底该回到哪?
单Activity架构,说白了就是整个应用只有一个Activity,所有页面都用Fragment来承载。
这样做的好处很明显:
- 返回栈统一管理:所有页面都在同一个返回栈里,逻辑清晰
- 数据共享方便:ViewModel可以跨Fragment共享,不用搞什么全局变量
- 动画过渡自然:Fragment之间的转场动画比Activity切换流畅得多
- 生命周期可控:Fragment的生命周期比Activity更精细,你能做更多事情
我的建议:新项目直接上单Activity架构。老项目迁移?别急,慢慢来。我曾经把一个6个Activity的项目改成单Activity,花了整整两周。但改完之后,维护成本直线下降。
Navigation组件解决了什么问题?
好,现在咱们聊聊Navigation组件到底解决了哪些痛点。
痛点一:Fragment事务管理
以前写Fragment跳转,你得写:
supportFragmentManager.beginTransaction()
.replace(R.id.container, DetailFragment.newInstance(id))
.addToBackStack(null)
.commit()
代码倒是不长,但问题在于:你没法直观地看到页面之间的关系。改个流程,得翻代码找半天。
Navigation组件怎么做的?你只需要在导航图里画一条线:
<fragment
android:id="@+id/listFragment"
android:name=".ListFragment">
<action
android:id="@+id/toDetail"
app:destination="@id/detailFragment" />
</fragment>
然后在代码里调用:
findNavController().navigate(R.id.toDetail)
完事。简单到令人发指。
痛点二:参数传递
以前传参,用Bundle。取参的时候,还得判空。一不小心就NullPointerException。
Navigation组件用Safe Args插件,自动生成参数类。类型安全,编译期就能发现问题。
// 定义参数
<argument
android:name="productId"
app:argType="integer" />
// 使用参数
val args: DetailFragmentArgs by navArgs()
val productId = args.productId
我曾经在项目里因为传参类型写错,线上崩了两次。用了Safe Args之后,再也没出过这种问题。
痛点三:深层链接
以前做深层链接,得写IntentFilter,解析URL,手动跳转。代码分散在各处,维护起来很痛苦。
Navigation组件原生支持深层链接。你只需要在导航图里配置:
<deepLink app:uri="myapp://product/{productId}" />
然后系统会自动帮你解析并跳转到对应的Fragment。省心省力。
痛点四:返回栈管理
这个我深有体会。以前做购物车流程,用户从商品列表进详情,加购物车,去结算,支付成功。如果支付失败,要回到购物车页面。如果支付成功,要回到商品列表。
这种复杂的返回栈逻辑,用传统方式写,代码量巨大,还容易出bug。
Navigation组件提供了popUpTo和popUpToInclusive,可以精确控制返回栈的行为:
findNavController().navigate(
R.id.toPaymentResult,
null,
NavOptions.Builder()
.setPopUpTo(R.id.cartFragment, true)
.build()
)
一行代码,搞定复杂的返回逻辑。
注意:Navigation组件虽然强大,但也不是银弹。如果你的应用页面之间耦合度极高,或者需要大量自定义动画,可能还是得手写Fragment事务。不过,90%的场景下,Navigation组件都够用了。
知识体系总览
下面这张图,是我自己整理的Navigation组件知识体系。你可以把它当作学习路线图:
这张图把Navigation组件的核心内容分成了三块:核心组件、解决痛点、架构基础。你顺着这个脉络学,思路会清晰很多。
小结
Navigation组件不是什么高深的技术。它就是把我们以前手动做的事情,封装成了标准化的工具。你只要学会怎么用,就能省下大量时间。
我个人觉得,Navigation组件最大的价值不是技术本身,而是它强制你思考页面之间的关系。以前写代码,页面跳转是散落的。用了Navigation,你得先画导航图,想清楚每个页面怎么连。这个思考过程,本身就是一种架构设计。
好了,这一章就到这里。下一章咱们会手把手搭建一个单Activity项目,把Navigation组件真正用起来。
公众号:蓝海资料掘金营,微信deep3321