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组件知识体系 核心组件 NavHost NavController NavGraph 解决痛点 Fragment事务管理 参数传递与类型安全 深层链接 返回栈管理 架构基础 单Activity Fragment承载 ViewModel共享 目标:简化导航,提升开发效率

这张图把Navigation组件的核心内容分成了三块:核心组件、解决痛点、架构基础。你顺着这个脉络学,思路会清晰很多。

小结

Navigation组件不是什么高深的技术。它就是把我们以前手动做的事情,封装成了标准化的工具。你只要学会怎么用,就能省下大量时间。

我个人觉得,Navigation组件最大的价值不是技术本身,而是它强制你思考页面之间的关系。以前写代码,页面跳转是散落的。用了Navigation,你得先画导航图,想清楚每个页面怎么连。这个思考过程,本身就是一种架构设计。

好了,这一章就到这里。下一章咱们会手把手搭建一个单Activity项目,把Navigation组件真正用起来。


公众号:蓝海资料掘金营,微信deep3321