| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> 【Jetpack-Navigation】想去哪就去哪,Android世界的指南针 -> 正文阅读 |
|
[移动开发]【Jetpack-Navigation】想去哪就去哪,Android世界的指南针 |
前言
很多人不喜欢 Navigation 因为其设计不符合开发者的预期,它在管理「平级界面」时来回切换会导致平级的 fragment 重建。网上针对这一问题有一个 重写 Navigator 的方案,大多数人会简单地认为 Navigation 无法保存 fragment 状态是因为使用了 replace(曾经的我也这样认为) 本文的内容为 Navigation 的职能边界,简单使用,高阶使用技巧(例如同一 activity 部分内部分 fragment 共享 ViewModel,模块化)以及关于 Navigation 所谓的「设计问题」的探讨 对 Navigation 的使用及职能边界已经了解的小伙伴可以直接跳过前两部分 由于文章内容较长,已将模块化部分单独成文,地址在这 没有 Navigation 的世界Android 中,activity 和 fragment 是主要的视图控制器,因此界面间的调转也是围绕 activity / fragment 进行的
如果项目比较大,我们可能会将 KEY 抽取为常量,并且在 activity 和 fragment 中填写静态方法以告诉调用者该界面需要什么参数
可以看到,得益于 kotlin 的扩展函数,界面间跳转的代码已足够简洁 但是
Navigation 简介Jetpack 导航组件是一套库,工具和指南,为应用内导航提供了强大的导航框架 它是一套库,封装着应用内导航的 API引入依赖如下
它支持 fragment ,activity,或者是自定义的 destination 间的跳转 Navigation UI 库 支持 Drawer,Toolbar 等 UI 组件 它是一套工具,在 Android Studio 中可以可视化管理界面的导航逻辑Android Studio 提供可视化管理的工具 现在我们对 Navigation 有一个初步的认识,接下来我们看看 Navigation 的职能边界 Navigation 能做什么
Navigation 工作逻辑Navigation 主要有三个部分
Navigation Graph
「Navigation Graph 中的每一个界面叫:Destination」,它可以使 fragment ,activity,或者自定义的 Destination Navigation 管理的就是 Destination 间的跳转 点击 Destination,可以在屏幕右侧看见 deep link 等信息的配置 「Navigation Graph 中的带箭头的线叫:Action」,它代表着 Destination 间不同的路径 点击 Action,可以在屏幕右侧看到 Action 的详细配置,动画,Destination 间跳转传递的参数,操作返回栈,Launch Options
NavHost
NavHostFragment 是 navigation-fragment 中的类 它提供了一个可独立导航的区域,使用时大概是这样 NavHostFragment 使用 所有的 fragment Destination 都是通过 NavHostFragment 管理,相当于一个容器 每个 NavHostFragment 都有一个 NavController,用于定义 navigation host 中的导航。 它还包括 navigation graph 以及 navigation 状态(例如当前位置和返回栈),它们将与 NavHostFragment 本身一起保存和恢复 NavController
其中 NavGraph 决定了界面间的跳转逻辑,它通常在 Android resource 下创建,同时也支持通过代码动态创建 Navigator 定义了一在应用内导航的机制。它的实现类有 ActivityNavigator, DialogFragmentNavigator, FragmentNavigator, NavGraphNavigator。当然,开发者也可以自定义 总结下面引用一张 KunMinX 的专栏 重学 Android Navigation 一文中的配图,帮助大家理解这其中的依赖关系
我们在 res/navigatoin 创建的 xml 文件叫 其内部每个节点叫 连接
Navigation 的使用技巧Dialog DestinationNavigation 2.1.0 引入,用于实现 navigate 到一个 DialogFragment 使用也很简单,使用 dialog 标签,其他处理的与 fragment 相同 同一 graph 中共享 ViewModel我们都知道 fragment 可以使用 activity 级别共享 ViewModel,但是对于单 activity 项目,这就意味着所有的 fragment 都能拿到这个共享的 ViewModel。本着最少知道原则,这不是一个好的设计
Navigation 2.1.0,官方引入了 navigation graph 内共享的 ViewModel,这使得 ViewModel 的作用域得到了细化,业务之间可以很好地被隔离 使用起来非常简单
Safe Args什么是 Safe Args? 它是一个 Gradle 插件,可以根据 navigation 文件生成代码,帮助开发者安全地在 那么为什么要设计这样一个插件呢? 我们知道使用 bundle 或者 intent 传递数据时,如果出现类型不匹配或者其他异常,是在 Runtime 中才能发现的,而 使用
前面我们提到 如果要生成 java 代码,则在 app 或其他 module 的 build.gradle 中加入
如果向生产 kotlin 代码,则加入
参数是在 action 中配置的,我们只需在加入 argument 标签
Navigation 支持以下类型 启用 Safe Args 后,生成的代码将为每个操作以及每个发送和接收 destination 创建以下类型安全的类和方法
下面的代码展示如何在发送 destination 传递参数
接下来展示如何在接收 destination 中取出传来的参数,kotlin 可以使用
嵌套 navigation graph有一些 destination 通常是组合使用,并且在多个地方重复调用。例如独立的登录流程,后续的忘记密码,修改密码等 destination 可以看做一个整体来使用 这种情况我们使用嵌套 navigation graph,选中一组可以作为整体的 destination (按住 shift 鼠标点选),然后右击选中 「Move to Nested Graph」 > 「New Graph」 。这样就生成了 嵌套 navigation graph。在 嵌套 navigation graph 上双击即可查看内部的 destination 「如果想要引用其他 module 中的 graph,可以使用 include 使用
全局 action您可以为多个 destination 创建共用的 action,例如您可能想要在不同的 destination 中导航至相同的界面 对于这种情况您可以使用全局 action 选中一个 destination 并右击,选择 「Add Action > Global」 ,一个箭头会 出现在 destination 的左边 使用也很简单,向 navigate 方法传入全局 action 的资源 id 即可
条件导航在开发过程中,我们可能会遇到一个 destination 根据条件跳转不同的 destination 的情况 例如一些 destination 需要用户处于登录状态才能进入,或者在游戏结束后,胜利和失败跳转不同的 destination 下面我们用一个示例来展示 Navigation 如何处理该种场景 该示例中,用户尝试跳转到资料页中,如果该用户处于未登录状态,则需要跳转到登录界面 我们使用 LoginViewModel 来保存登录状态,从 ProfileFragment 点击按钮跳转到 ProfileDetailFragment,在该界面判断登录的状态,如果是未授权则跳转到 LoginFragment,如果已授权则提示欢迎 在登录界面判断登录状态,如果授权成功则回到 ProfileDetailFragment,授权失败则显示登录失败提示 在登录界面点击返回视为未授权,应该直接返回 ProfileFragment 界面 Deep Links开发过程中我们可能会遇到这类的需求,我们需要让用户打开 app 时直接空降到某个特定页面(例如点开通知栏跳转到特定文章),亦或者我们需要从一个 destination 跳转到一个在其他流程中比较深的位置的 destination ,如下图,从 FriendList 跳转到 Chat 界面 上面的这种需求叫作 显式 deep link在 manifest activity -> intent-filter 标签下 加入 action ,category ,data 等标签,满足条件的 intent 可以被打开 详情见 官方文档,这里不再赘述,我们只关注如何通过 Navigation 构建 intent
如果已经存在 NavController,可以通过 NavController.createDeepLink() 方法创建 deep link 隐式 deep link在 navigation graph 中支持 deepLink 标签 例如上图从 FriendListFragment 跳转到 ChatFragment,可以在 graph ChatFragment 下加入 deepLink 标签
因此可直接调用
模块化参见 【奇技淫巧】使用 Navigation + Dynamic Feature Module 实现模块化 Navigation 设计探讨fragment replace 你真的了解吗androidx 下 frament replace 的行为你真的了解吗? 该部分内容我们在 【背上 Jetpack】绝不丢失的状态 androidx SaveState ViewModel-SaveState 分析 和【背上 Jetpack 之 Fragment】从源码角度看 Fragment 生命周期 AndroidX Fragment1.2.2 源码分析 已有分析 因此我们直接说结论 fragment replace 后 「前一个 fragment 会执行 onDestroyView 而不执行 onDestroy」 ,即 fragment 本身未销毁,其内部 view 被销毁
那么 Navigation 的所谓 「设计问题」是怎么回事? 被重建的 fragment我们通过 Android Studio 创建项目,模版选择 Bottom Navigation Activity,可以得到使用 Navigation 实现的 平级 tab 切换模版。我们使用从 HomeFragment 切换至 DashboardFragment,之后切换回 HomeFragment。日志如下 可以看到 HomeFragment 被重建了,「原实例(e6c266),新实例(c3e49cc)」 「fragment 被重建,这就是原因所在!」 那么为什么出现这种现象?我们翻一下源码 从源码可以看到,其内部通过反射创建了新的 fragment 实例,这导致 fragment 内部的状态无法恢复 不过如果 navigation 导航的所有 destination 没有平级关系,换句话说在一个返回栈内,这样的设计是没有问题的 但是有些时候我们希望使用 navigation 管理一些平级界面,例如 BottomNavigation 不符合 Material Design 的 BottomNavigationissuetracker 有这样一个 issue,注意提出的时间 主要意思就是现阶段的 bottom tab navigation 不符合 Material Design 的规范
相同类型的 issue 还有这些 Ian Lake 在该条 issue 下给出了详细的解答 我在这里简单介绍一下 Ian Lake,虽然不知道他的职位,但从他的活跃程度看应该是 fragment 和 navigation 的负责人,在 Google I/O 大会 和 Android Dev Summit 多次进行演讲,例如 fragment 的过去,现在和将来,单 activity 项目 「一句话解释:单个 FragmentManager 不支持多个返回栈,以现有的 fragment API 无法做到这一支持,开发者不得不自己实现多返回栈(例如我在 【背上 Jetpack 之 Fragment】从源码的角度看 Fragment 返回栈 附多返回栈 demo 一文中提供的 demo)」 但他提供了短期和中期方案
以上回复发生在 2019 年 2 月 在 2019 年 10 月 Ian Lake 再次回复了开发者的疑问 多返回栈支持计划需要三步走
之后他回复了 70 楼的开发者,明确了如果使用单个 NavHostFragment / navigation graph / FragmentManager 能够支持多返回栈,那么从 A 或 B 导航到 C 就不会有任何问题 接着时间来到了 2020 年 今年 1 月 30 日,Ian Lake 再次做了回复 大概意思就是原定 至此关于 Navigation 所谓的 「设计问题」就探讨结束了 Jetpack组件库系列入门教程:【我的Github】 欢迎各位小伙伴在评论区留言说说你的想法 |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 11:09:19- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |