返回顶部
首页 > 资讯 > 移动开发 >Android多返回栈技术
  • 473
分享到

Android多返回栈技术

2024-04-02 19:04:59 473人浏览 八月长安
摘要

目录1、系统返回按钮的乐趣2、Fragment 中的多返回栈3、排除 Fragment 在技术上的障碍4、Fragment 中值得期待的地方4、使用 Navigation 将多返回栈

户通过系统返回按钮导航回去的一组页面,在开发中被称为返回栈 (back stack)。多返回栈即一堆 "返回栈",对多返回栈的支持是在 Navigation 2.4.0-alpha01 和 Fragment 1.4.0-alpha01 中开始的。本文将为您展开多返回栈的技术详解。

1、系统返回按钮的乐趣

无论您在使用 Android 全新的 手势导航 还是传统的导航栏,用户的 "返回" 操作是 Android 用户体验中关键的一环,把握好返回功能的设计可以使应用更加贴近整个生态系统。

在最简单的应用场景中,系统返回按钮仅仅 finish 您的 Activity。在过去您可能需要覆写 Activity 的 onBackPressed() 方法来自定义返回操作,而在 2021 年您无需再这样操作。我们已经在OnBackPressedDispatcher 提供了针对自定义返回导航的api。实际上这与 FragmentManager 和 NavController 中 已经 添加的 API 相同。

这意味着当您使用 Fragments 或 Navigation 时,它们会通过 OnBackPressedDispatcher 来确保您调用了它们返回栈的 API,系统的返回按钮会将您推入返回栈的页面逐层返回。

多返回栈不会改变这个基本逻辑。系统的返回按钮仍然是一个单向指令 —— "返回"。这对多返回栈 API 的实现机制有深远影响。

2、Fragment 中的多返回栈

在 surface 层级,对于 多返回栈的支持 貌似很直接,但其实需要额外解释一下 "Fragment 返回栈" 到底是什么。FragmentManager 的返回栈其实包含的不是 Fragment,而是由 Fragment 事务组成的。更准确地说,是由那些调用了 addToBackStack(String name)) API 的事务组成的。

这就意味着当您调用 commit() 提交了一个调用过 addToBackStack() 方法的 Fragment 事务时,FragmentManager 会执行所有您在事务中所指定的操作 (比如 替换操作),从而将每个 Fragment 转换为预期的状态。然后 FragmentManager 会将该事务作为它返回栈的一部分。

当您调用 popBackStack() 方法时 (无论是直接调用,还是通过系统返回键以 FragmentManager 内部机制调用),Fragment 返回栈的最上层事务会从栈中弹出 -- 比如新添加的 Fragment 会被移除,隐藏的 Fragment 会显示。这会使得 FragmentManager 恢复到最初提交 Fragment 事务之前的状态。

注: 这里有一个非常重要的事情需要大家注意,在同一个 FragmentManager 中绝对不应该将含有 addToBackStack() 的事务和不含的事务混在一起: 返回栈的事务无法察觉返回栈之外的 Fragment 事务的修改 —— 当您从堆栈弹出一个非常不确定的元素时,这些事务从下层替换出来的时候会撤销之前未添加到返回栈的修改。
也就是说 popBackStack() 变成了销毁操作: 任何已添加的 Fragment 在事务被弹出的时候都会丢失它的状态。换言之,您会失去视图的状态,任何所保存的实例状态 (Saved Instance State),并且任何绑定到该 Fragment 的 ViewModel 实例都会被清除。这也是该 API 和新的 saveBackStack() 方法之间的主要区别。saveBackStack() 可以实现弹出事务所实现的返回效果,此外它还可以确保视图状态、已保存的实例状态,以及 ViewModel 实例能够在销毁时被保存。这使得 restoreBackStack() API 后续可以通过已保存的状态重建这些事务和它们的 Fragment,并且高效 "重现" 已保存的全部细节。太神奇了!

而实现这个目的必须要解决大量技术上的问题。

3、排除 Fragment 在技术上的障碍

虽然 Fragment 总是会保存 Fragment 的视图状态,但是 Fragment 的 onSaveInstanceState() 方法只有在 Activity 的 onSaveInstanceState() 被调用时才会被调用。为了能够保证调用 saveBackStack() 时 SavedInstanceState 会被保存,我们 还 需要在 Fragment 生命周期切换 的正确时机注入对 onSaveInstanceState() 的调用。我们不能调用得太早 (您的 Fragment 不应该在 STARTED 状态下保存状态),也不能调用得太晚 (您需要在 Fragment 被销毁之前保存状态)。

这样的前提条件就开启了需要 解决 FragmentManager 转换到对应状态的问题,以此来保障有一个地方能够将 Fragment 转换为所需状态,并且处理可重入行为和 Fragment 内部的状态转换。

在 Fragment 的重构工作进行了 6 个月,进行了 35 次修改时,发现 Postponed Fragment 功能已经严重损坏,这一问题使得被推迟的事务处于一个中间状态 —— 既没有被提交也并不是未被提交。之后的 65 个修改和 5 个月的时间里,我们几乎重写了 FragmentManager 管理状态、延迟状态切换和动画的内部代码,具体请参见我们之前的文章《全新的 Fragment: 使用新的状态管理器》。

4、Fragment 中值得期待的地方

随着技术问题的逐步解决,包括更加可靠和更易理解的 FragmentManager,我们新增加了两个 API: saveBackStack() 和 restoreBackStack()。

如果您不使用这些新增 API,则一切照旧: 单个 FragmentManager 返回栈和之前的功能相同。现有的 addToBackStack() 保持不变 —— 您可以将 name 赋值为 null 或者任意 name。然而,当您使用多返回栈时,name 的作用就非常重要了: 在您调用 saveBackStack() 和之后的 restoreBackStack() 方法时,它将作为 Fragment 事务的唯一的 key。

举个例子,会更容易理解。比如您已经添加了一个初始的 Fragment 到 Activity,然后提交了两个事务,每个事务中包含一个单独的 replace 操作:


// 这是用户看到的初始的 Fragment
fragmentManager.commit {
 setReorderingAllowed(true)
 replace<HomeFragment>(R.id.fragment_container)
}
// 然后,响应用户操作,我们在返回栈中增加了两个事务
fragmentManager.commit {
 setReorderingAllowed(true)
 replace<ProfileFragment>(R.id.fragment_container)
 addToBackStack(“profile”)
}
fragmentManager.commit {
 setReorderingAllowed(true)
 replace<EditProfileFragment>(R.id.fragment_container)
 addToBackStack(“edit_profile”)
}

也就是说我们的 FragmentManager 会变成这样:

△ 提交三次之后的 FragmentManager 的状态

比如说我们希望将 profile 页换出返回栈,然后切换到通知 Fragment。这就需要调用 saveBackStack() 并且紧跟一个新的事务:


fragmentManager.saveBackStack("profile")
fragmentManager.commit {
 setReorderingAllowed(true)
 replace<NotificationsFragment>(R.id.fragment_container)
 addToBackStack("notifications")
}

现在我们添加 ProfileFragment 的事务和添加 EditProfileFragment 的事务都保存在 "profile" 关键字下。这些 Fragment 已经完全将状态保存,并且 FragmentManager 会随同事务状态一起保持它们的状态。很重要的一点: 这些 Fragment 的实例并不在内存中或者在 FragmentManager 中 —— 存在的仅仅只有状态 (以及任何以 ViewModel 实例形式存在的非配置状态)。


△ 我们保存 profile 返回栈并且添加一个新的 commit 后的 FragmentManager 状态

替换回来非常简单: 我们可以在 "notifications" 事务中同样调用 saveBackStack() 操作,然后调用 restoreBackStack():


fragmentManager.saveBackStack(“notifications”)
fragmentManager.restoreBackStack(“profile”)

这两个堆栈项高效地交换了位置:

△ 交换堆栈项后的 FragmentManager 状态

维持一个单独且活跃的返回栈并且将事务在其中交换,这保证了当返回按钮被点击时,FragmentManager 和系统的其他部分可以保持一致的响应。实际上,整个逻辑并未改变,同之前一样,仍然弹出 Fragment 返回栈的最后一个事务。

这些 API 都特意按照最小化设计,尽管它们会产生潜在的影响。这使得开发者可以基于这些接口设计自己的结构,而无需通过任何非常规的方式保存 Fragment 的视图状态、已保存的实例状态、非配置的状态。

当然了,如果您不希望在这些 API 之上构建您的框架,那么可以使用我们所提供的框架进行开发。

4、使用 Navigation 将多返回栈适配到任意屏幕类型

Navigation Component 最初 是作为通用运行时组件进行开发的,其中不涉及 View、Fragment、Composable 或者其他屏幕显示相关类型及您可能会在 Activity 中实现的 "目的地界面"。然而,NavHost 接口 的实现中需要考虑这些内容,通过它添加一个或者多个 Navigator 实例时,这些实例 确实 清楚如何与特定类型的目的地进行交互。

这也就意味着与 Fragment 的交互逻辑全部封装在了 navigation-fragment 开发库和它其中的 FragmentNavigator 与 DialogFragmentNavigator 中。类似的,与 Composable 的交互逻辑被封装在完全独立的 navigation-compose 开发库和它的 ComposeNavigator 中。这里的抽象设计意味着如果您希望仅仅通过 Composable 构建您的应用,那么当您使用 Navigation Compose 时无需任何涉及到 Fragment 的依赖。

该级别的分离意味着 Navigation 中有两个层次来实现多返回栈:

  • 保存独立的 NavBackStackEntry 实例状态,这些实例组成了 NavController 返回栈。这是属于 NavController 的职责。
  • 保存 Navigator 针对每个 NavBackStackEntry 的特定状态 (比如与 FragmentNavigator 目的地相关联的 Fragment)。这是属于 Navigator 的职责。

仍需特别注意那些 尚未 更新的 Navigator,它们无法支持保存自身状态。底层的 Navigator API 已经整体重写来支持状态保存 (您需要覆写新增的 navigate() 和 popBackStack() API 的重载方法,而不是覆写之前的版本),即使 Navigator 并未更新,NavController 仍会保存 NavBackStackEntry 的状态 (在 Jetpack 世界中向后兼容是非常重要的)。

备注: 通过绑定 TestNavigatorState 使其成为一个 mini-NavController 可以实现在新的 Navigator API 上更轻松、独立地测试您自定义的 Navigator。
如果您仅仅在应用中使用 Navigation,那么 Navigator 这个层面更多的是实现细节,而不是您需要直接与之交互的内容。可以这么说,我们已经完成了将 FragmentNavigator 和 ComposeNavigator 迁移到新的 Navigator API 的工作,使其能够正确地保存和恢复它们的状态,在这个层面上您无需再做任何额外工作。

5、在 Navigation 中启用多返回栈

如果您正在使用 NavigationUI,它是用于连接您的 NavController 到 Material 视图组件的一系列专用助手,您会发现对于菜单项、BottomNavigationView (现在叫 NavigationRailView) 和 NavigationView,多返回栈是 默认启用 的。这就意味着结合 navigation-fragment 和 navigation-ui 使用就可以。

NavigationUI API 是基于 Navigation 的其他公共 API 构建的,确保您可以准确地为自定义组件构建您自己的版本。保证您可以构建所需的自定义组件。启用保存和恢复返回栈的 API 也不例外,在 Navigation XML 中通过 NavOptions 上的新 API,也就是 navOptions Kotlin DSL,以及 popBackStack() 的重载方法可以帮助您指定 pop 操作保存状态或者指定 navigate 操作来恢复之前已保存的状态。

比如,在 Compose 中,任何全局的导航模式 (无论是底部导航栏、导航边栏、抽屉式导航栏或者任何您能想到的形式) 都可以使用我们在与 底部导航栏集成 所介绍的相同的技术,并且结合 saveState 和 restoreState 属性一起调用 navigate():


onClick = {
 navController.navigate(screen.route) {
   // 当用户选择子项时在返回栈中弹出到导航图中的起始目的地
   // 来避免太过臃肿的目的地堆栈
   popUpTo(navController.graph.findStartDestination().id) {
     saveState = true
   }

   // 当重复选择相同项时避免相同目的地的多重拷贝
   launchSingleTop = true
   // 当重复选择之前已经选择的项时恢复状态
   restoreState = true
 }
}

5、保存状态,锁定用户

对用户来说,最令人沮丧的事情之一便是丢失之前的状态。这也是为什么 Fragment 用一整页来讲解 保存与 Fragment 相关的状态,而且也是我非常乐于更新每个层级来支持多返回栈的原因之一:

  • Fragments (比如完全不使用 Navigation Component): 通过使用新的 FragmentManager API,也就是 saveBackStack 和 restoreBackStack。
  • 核心的 Navigation 运行时: 添加可选的新的 NavOptions 方法用于 restoreState(恢复状态) 和 saveState (保存状态) 以及新的 popBackStack() 的重载方法,它同样可以传入一个布尔型的 saveState 参数 (默认是 false)。
  • 通过 Fragment 实现 Navigation: FragmentNavigator 现在利用新的 NavigatorAPI,通过使用 Navigation 运行时 API 将 Navigation 运行时 API 转换为 Fragment API。
  • NavigationUI: 每当它们弹出返回栈时,onNavDestinationSelected()、NavigationBarView.setupWithNavController() 和 NavigationView.setupWithNavController() 现在默认使用 restoreState 和 saveState 这两个新的 NavOption。也就意味着 当升级到 Navigation 2.4.0-alpha01 或者更高版本后,任何使用 NavigationUI API 的应用无需修改代码即可实现多返回栈。

到此这篇关于Android多返回栈技术的文章就介绍到这了,更多相关Android多返回栈内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Android多返回栈技术

本文链接: https://lsjlt.com/news/133452.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Android多返回栈技术
    目录1、系统返回按钮的乐趣2、Fragment 中的多返回栈3、排除 Fragment 在技术上的障碍4、Fragment 中值得期待的地方4、使用 Navigation 将多返回栈...
    99+
    2024-04-02
  • Android中Fragment的生命周期与返回栈的管理
    既然我们已经知道了Fragment很好用,那么我们也需要知道它的工作原理。Fragment只能存在于(作为容器的)Activity中,每一个Fragment都有自己的视图结构,...
    99+
    2022-06-06
    fragment Android
  • JavaScript的技术栈分析
    本篇内容介绍了“JavaScript的技术栈分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!ReactR...
    99+
    2024-04-02
  • android 多线程技术应用
    多线程案例——计时器 这个案例中,屏幕启动之后,进入如图所示的界面。 屏幕上有一个文本框用于显示逝去的时间,此外还有一个“停止计时”按钮。案例的用例图如图所示。  能...
    99+
    2022-06-06
    技术应用 多线程 线程 Android
  • 大数据技术栈浅述
    最近在做企业安全建设,企业安全建设中最常见的一项就是做监控,监控的种类多种多样,但是底层的技术栈却基本是一致的————大数据技术,下面我记录一下我最近学习到的一些大数据技术,下文只是描述个脉络而已。 大数据的技术栈,以及对应的上下...
    99+
    2014-10-01
    大数据技术栈浅述
  • python函数怎么返回多个返回值
    本篇内容主要讲解“python函数怎么返回多个返回值”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“python函数怎么返回多个返回值”吧!一般情况下,函数只有一个返回值,但Python也支持函数...
    99+
    2023-06-30
  • 如何理解IPv6过渡技术中的双栈技术
    这期内容当中小编将会给大家带来有关如何理解IPv6过渡技术中的双栈技术,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。       IPv6经过20多年的发展研究实验...
    99+
    2023-06-03
  • Oracle 闪回技术
    一、配置闪回数据库1、数据库处于归档日志模式SQL> select log_mode from v$database;  LOG_MODE     &...
    99+
    2024-04-02
  • web技术栈中的池有哪些
    本篇内容主要讲解“web技术栈中的池有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“web技术栈中的池有哪些”吧!连接池相信每一个程序员都不陌生,我们在使用...
    99+
    2024-04-02
  • Python爬虫技术栈 | urllib库&&urllib3库
    ❤️urllib库详解❤️ 每篇前言: 第一部分:urllib库 一、request模块:发送请求 1.urlopen() ...
    99+
    2023-09-10
    python 爬虫 urllib库urllib3库
  • Vue技术栈的相关知识点
    这篇文章主要为大家展示了Vue技术栈的相关知识点,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带大家一起来研究并学习一下“Vue技术栈的相关知识点”这篇文章吧。vue笔记一:Vue技术栈1、node.js如果想要javasc...
    99+
    2023-06-28
  • Django Web开发技术栈清单-P
    问题答案由本人整理 1.基础语法是否熟悉?介绍一下 Python和其他语言最大的区别就是使用行和缩进,而不是大括号({})或者分号(;)来控制类、函数或者逻辑判断。Python使用换行来表示语句的结束。但同时可以用左斜杠()将一行语句分...
    99+
    2023-01-31
    清单 技术 Django
  • TICK技术栈(三)InfluxDB安装及使用
    1.什么是InfluxDB? InfluxDB是一个用Go语言开发的时序数据库,用于处理高写入和查询负载,专门为带时间戳的数据编写,对DevOps监控,IoT监控和实时分析等应用场景非常有用。通过自定义配置让InfluxDB保留规定时间内的...
    99+
    2021-02-26
    TICK技术栈(三)InfluxDB安装及使用
  • TICK技术栈(二)Telegraf安装及使用
    1.什么是Telegraf? Telegraf是一个用Go语言开发的代理程序,可用于收集和报告指标。Telegraf插件直接从其运行的系统中获取各种指标,从第三方API中提取指标,甚至通过StatsD和Kafka消费者服务来监听指标。它还具...
    99+
    2021-02-01
    TICK技术栈(二)Telegraf安装及使用
  • TICK技术栈(五)Kapacitor安装及使用
    1.什么是Kapacitor? Kapacitor是InfluxData开源的数据处理引擎。它可以处理来自InfluxDB的流数据和批处理数据,并且用户可以用tickScript脚本来处理,监视和警报时序数据库中的时序数据。github地址...
    99+
    2021-11-17
    TICK技术栈(五)Kapacitor安装及使用
  • TICK技术栈(四)Grafana安装及使用
    1.什么是Grafana? Grafana是一款采用go语言和Angular框架编写的开源的可视化工具,主要用于大规模指标数据的可视化展示,提供包括折线图,饼图,仪表盘等多种监控数据可视化UI,是网络架构和应用分析中最流行的时序数据可视化工...
    99+
    2018-11-08
    TICK技术栈(四)Grafana安装及使用
  • 大数据技术栈,主要有哪些
    往大数据方向发展需要学哪些技术?网上一搜真是指不胜屈。对于小白来说,实在是一头雾水,到底哪些是当下流行的?哪些是必须要先学会的?流行?主次搞不清。为了解决这些疑惑,羚羊专门花了些时间,  挨个技术去研究对比归类,大概总结出以下的技...
    99+
    2021-09-27
    大数据技术栈,主要有哪些
  • java最常用的技术栈是什么
    这篇文章主要介绍“java最常用的技术栈是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“java最常用的技术栈是什么”文章能帮助大家解决问题。java常用的技术栈有:1、JDK;2、Web框架;...
    99+
    2023-07-04
  • Android实现返回键功能
    记录用户点击的操作历史,使用栈数据结构,频繁的操作栈顶(添加,获取,删除),使用LinkedList 捕获用户的返回键操作,响应返回键,返回上一个界面 MainActivity...
    99+
    2022-06-06
    Android
  • python如何返回多个值
    小编给大家分享一下python如何返回多个值,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!返回多个值Python中的函数在没有字典,列表和类的情况下可以返回多个变...
    99+
    2023-06-27
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作