Android multiple back stacks導航的幾種實現

来源:https://www.cnblogs.com/mengdd/archive/2022/06/25/solutions-of-android-multi-stack-navigation.html
-Advertisement-
Play Games

Android multiple back stacks導航 談談android中多棧導航的幾種實現. 什麼是multiple stacks 當用戶在app里切換頁面時, 會需要向後回退到上一個頁面, 頁面歷史被保存在一個棧里. 在Android里我們經常說"back stack". 有時候在app ...


Android multiple back stacks導航

談談android中多棧導航的幾種實現.

什麼是multiple stacks

當用戶在app里切換頁面時, 會需要向後回退到上一個頁面, 頁面歷史被保存在一個棧里.
在Android里我們經常說"back stack".

有時候在app里我們需要維護多個back stack, 比較典型的場景是bottom navigation bar或者側邊的drawer.

如果需求要求在切換tab的時候保存每個tab上的歷史, 這樣當用戶返回的時候還是返回到上次離開的地方, 這種就叫multiple stacks.

(與之對應的single stack行為是返回之後回到了tab首頁.)

本文之後的內容都以bottom bar的多棧導航為例.

multi-stack的需求

首先還是討論一下需求.

當bottom bar不支持多棧時, 當點擊切換底部tab, 再返回原來的tab, 所有在之上打開的頁面都會消失, 只有第一層(根)頁面會顯示.

這也是可以接受的, 甚至在material design裡面作為Android平臺的預設行為被提及: material design

但它同時也說了, 如果需要的話, 這個行為是可以被改的.

如果你想保留用戶在上個tab看過的內容狀態, 很可能就需要做multi-stack, 每個tab上的棧是獨立退出, 分別保留的.

通常, 這還不是僅有的需求.

如果用戶點擊已選中的tab, 需要重置這個stack嗎?

需要定製轉場動畫嗎?

需要保留tab歷史嗎? 比如從tab A -> B -> C, 在C的根頁面back, 是想回到B還是回到home tab?

在bottom navigation的預設實現中(用Android Studio創建一個Bottom Navigation的新項目), 在非home tab的根節點, 點擊back, 總是先回到home tab, 再次back才會退出app.
因為這樣是符合固定start destination的原則的. 用戶在打開後和關閉前, 看到的是同一個頁面.

但是如果你有保存tab歷史的需求, 也可以考慮如何定製它.

當你更進一步地涉及到實現層面, 你會遇到更多實際操作的問題, 比如怎麼把一個詳情頁push到一個指定的棧, 如何pop destination.

讓我們列一下幾個需求點:

  • 維護多個棧.
  • 切換tab: 手動點擊tab或者其他tab內的交互. 比如dashboard跳轉到某個內容tab.
  • Push/pop destinations.
  • 重選(reselect)tab會重置該棧. (clear history.)
  • 轉場動畫
  • tab歷史.

技術背景

要進行導航的選型, 首先確定一下你的"destination"是什麼.

是composable還是fragment, 或者乾脆是View, 解決方案可能有很大的不同.

以這篇文章的scope來說, 我們就關註一個傳統的android app, 用Activity和Fragment實現.
所以bottom tab上的tab內容, 是不同Fragment.

Fragment lifecycle

為什麼這裡要提一下Fragment的生命周期呢?

因為fragment的生命周期和它的ViewModel緊密關聯, 進一步關係到了在導航過程中我們是否需要關註fragment的狀態恢復和刷新.

首先複習一下Fragment生命周期的回調: 什麼時候onDestroy會被調用?

  • replacetransaction沒有addToBackStack().
  • 當fragment被removed或者被popBackStack().

replacetransaction加上addToBackStack(), 舊的fragment會被壓入棧, 但它的生命周期只調用到onDestroyView().
當在它之上的其他fragment pop出來以後, 舊的這個fragment實例依然是同一個, 它重新顯示, 重新從onCreateView()開始走.

這是我們在single back stack下預期的行為.

ViewModel的生命周期和Fragment是對齊的, 也即Fragment的onDestroy()調用時, ViewModel的onCleared()被調用.

在導航切換目的地時, 如果fragment被destroy了, 我們可以保存一些關註的變數在saved instance bundle或者SavedStateHandle里, 用於之後的狀態恢復.
但是如果fragment沒有被destroy, 我們可以剩下不少力氣做這些狀態恢復.

所以理想的狀態是, 壓棧後的fragment實例不會被銷毀重建.

為了比較不同的解決方案, 我把一些sample放在了一起: https://github.com/mengdd/bottom-navigation-samples

Jetpack navigation component

官網: https://developer.android.com/guide/navigation

即便在FragmentManager的文檔 里, 也建議開發者使用jetpack的navigation library來處理app的navigation.

multiple back stack的支持是Navigation 2.4.0-alpha01Fragment 1.4.0-alpha01才加的.

試了下這個 demo,
代碼非常簡單, 我們基本什麼都不用做.

關於這裡面的思想可以看這篇文章: https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134

優點:

  • 最知名, 畢竟是官方的庫.
  • 支持類型安全的參數.
  • NavigationController支持pop到一個指定的destination.
  • 可以和Compose navigation庫一起使用.

缺點:

  • Multi-stack的支持: 當切換tab時, 前一個tab上的所有fragment都會被destroy, 當返回tab時棧內fragment會重建. 所以狀態會丟, 頁面可能會刷新.
  • 每個tab都需要是一個內嵌的navigation graph, 如果有一些common的destination, 需要include到每個graph中去. xml的navigation文件感覺很像一個大塊的樣板代碼.

FragmentManager

如果我們想做更多的定製, 我們可以考慮用FragmentManager的新APIs自己手動實現.

在文檔中doc 介紹的:

FragmentManager allows you to support multiple back stacks with the saveBackStack()
and restoreBackStack() methods. These methods allow you to swap between back stacks by saving one back stack and restoring a different one.

這是navigation component實現中實現多棧導航使用的方法.
所以也可以解釋為什麼切tab的時候fragment都被銷毀了.

saveBackStack() works similarly to calling popBackStack() with the optional name
parameter: the specified transaction and all transactions after it on the stack are popped.
The difference is that saveBackStack() saves the state of all fragments in the popped transactions.

優點:

  • 精細控制, 開發者獲得更多控制, 也更明白到底是怎麼回事.
  • 如果我們當前項目沒有採用任何navigation library, 都是手動跳轉, 採用這種方法我們就不用考慮遷移navigation.

缺點:

  • 要寫很多fragment transaction的樣板代碼.
  • 和navigation components一樣: 多棧實現中在切換棧時, 在舊的tab上的Fragments會被銷毀, 返回時全部重建.

Enro

https://github.com/isaac-udy/Enro

對於多module的大型項目來說, 我很推薦這個庫, 它可以幫助我們解耦module間的依賴.

multi-stack的demo

優點:

  • 基於註解, 所以要寫的代碼很少, 導航使用很方便.
  • 多module項目解耦.
  • 傳類型安全的參數和返回結果都很容易.
  • 可以在ViewModel中獲取navigation handle, 獲取參數.
  • 支持Compose做節點.
  • 對Unit Test也有一個輔助測試的依賴.
  • multi-stack support: 保持了切換tab的時候fragment實例.

缺點:

  • 可能目前還不是很知名. 需要說服別人學和採用這個.
  • Fragment的multi-stack: 不能rest stack到根節點. (嘗試了一下定製這個行為, 有點難).

Simple-stack

https://github.com/Zhuinden/simple-stack

這裡推薦一下這個庫作者的文章Creating a BottomNavigation Multi-Stack using child Fragments with Simple-Stack.
關於如何用simple-stack來做multi-stack.

最開始作者展示了一個不用任何庫, 僅用child fragments來實現的版本.

這是手動實現的另一種思想了.

後來才引入了用simple-stack做的demo
這是採用了原作者提供的sample, 比較簡單, 試了一下以後我發現可能還需要添加更多的代碼, 來做實際的應用.
比如詳情頁需要獲得某個tab的local stack的實例, 從而把自己push上去.

優點:

  • 作者在社區十分活躍, 有很多視頻和文章介紹simple-stack這個庫. 所以社區支持挺好.
  • multi-stack support: 保持了切換tab的時候fragment實例.
  • 支持控制和清空棧的歷史.
  • 有compose的擴展.

缺點:

  • 如果你的bottom bar當前是在activity的佈局里, 你需要把bottom bar和相關的東西都挪進一個RootFragment, 作為總的節點.
  • 作者提供的multi-stack sample還非常簡單, 需要寫更多的代碼來或者當前正確的棧來做push和pop操作. 不瞭解這個庫可能會寫得很醜.

其他庫

還有一些庫, 不是通用的navigation解決方案, 而只是為多棧導航設計的小庫.
比如:

這些庫都自帶sample.

優點:

  • 實現簡單, 只用幾個類. 如果我們想定製我們可以用這個代碼.
  • 要改動的範圍可以限制在bottom navigation的部分, 而不是整體改變navigation方案.

缺點:

  • 這些庫都不是很出名, 有不再維護的風險.
  • 可能和其他的navigation方案不能相容, 比如Navigation Components. 需要考慮整體.

總結

android (fragment實現) multi-stack navigation的可能解決方案:

方案 流行 整體方案 活躍 支持清空棧 Fragment被保存, 不被銷毀 支持Multi-modules Compose擴展
Jetpack Navigation Components 官方, 最出名 Yes Yes Yes No Yes Yes
Fragment Manager Android SDK - Yes Yes No No -
Enro Star: 188 Yes Yes No Yes Yes Yes
Simple Stack Star: 1.2k Yes Yes Yes Yes Yes Yes
Child Fragments Android SDK - Yes Yes Yes No -
JetradarMobile/android-multibackstack Star: 224 No No Yes No No -
DimaKron/Android-MultiStacks Star: 32 No Not sure Yes Yes No -

註意:

  • 整體方案: 表示該方案可以用於app整體的navigation解決方案, 而不僅僅是解決multi-stack的問題.
  • Fragment被保存, 不被銷毀: 當跳轉或者切tab時, 被壓入棧中的fragments不會被destroyed. 多棧支持的情況下, 儘管fragment被返回時都會被重建, 但是如果它不被銷毀, 我們就不需要做額外的工作來緩存狀態.

References:

作者: 聖騎士Wind
出處: 博客園: 聖騎士Wind
Github: https://github.com/mengdd
微信公眾號: 聖騎士Wind
微信公眾號: 聖騎士Wind
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、簡介 vmware為我們提供了三種網路工作模式,它們分別是:Bridged(橋接模式)、NAT(網路地址轉換模式)、Host-Only(僅主機模式)。 查看網路連接 打開vmware虛擬機,我們可以在選項欄的“編輯”->“虛擬網路編輯器”中看到VMnet0(橋接模式)、VMnet1(僅主機模式) ...
  • 安裝docker後,提示需要啟用hyper-v,在控制面板中勾選Hyper-v,然後重啟,更新快完成就提示無法完成功能配置,正在撤銷更改 解決方法 方法1 控制面板一個一個選 方法2 百度了n多內容,命令行什麼的都試了下,但是都沒有起作用.有說禁用了服務,看了下,確實是禁用了,但是開起來後還是不行. ...
  • 這玩意搞了我今天,直接裂開!系統更新根本解決不了 好在查了相關資料才知道,原來微軟在 Win10 的更新中,將搜索功能和語音助手 Cortana 進行了拆分,搜索成了一個獨立的功能,還好有外媒發現問題原因是 Bing 和 Cortana 集成造成,而且修複很簡單。 方法1 使用電腦管家修複; 方法2 ...
  • 四、Nginx 4.1、概述 4.1.1、介紹 Nginx是一款輕量級的Web伺服器/反向代理伺服器/電子右鍵(IMAP/POP3)代理伺服器。其特點是占有記憶體少,併發能力強,事實上Nginx的併發能力在同類型的網頁伺服器中表現較好,中國大陸使用Nginx的網站有:百度、就京東、新浪、網易、騰訊、淘 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 CentOS 1.備份原來的源 在控制台輸入mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup備份原本的源 2、下載新的 CentOS-Bas ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 一、環境要求 系統版本:CentOS7.x版本 硬體配置:記憶體2GB以上 cpu2核以上 硬碟大於30G 集群網路配置:集群中所有伺服器內網必須互通,並且需要訪問外網來拉取鏡像 禁用swap分區 二、k8s基礎環境操作: 1、關閉防火牆: [ro ...
  • 程式包編譯安裝的步驟: 源代碼-->預處理-->編譯-->彙編-->鏈接-->執行 多文件:文件中的代碼之間,很可能存在跨文件依賴關係 編譯源碼的項目工具 使用相關的項目管理工具可以大大減少編譯過程的複雜度 根據源碼類型來對這些工具進行分類: C、C++的源碼編譯:使用 make 項目管理器 con ...
  • 1、簡述 binlog 二進位日誌文件,這個文件記錄了MySQL所有的DML操作。通過binlog日誌我們可以做數據恢復,增量備份,主主複製和主從複製等等。 2、Docker中無法使用vim問題解決 https://blog.csdn.net/Tomwildboar/article/details/ ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 準備開個系列來聊一下 PerfView 這款工具,熟悉我的朋友都知道我喜歡用 WinDbg,這東西雖然很牛,但也不是萬能的,也有一些場景他解決不了或者很難解決,這時候藉助一些其他的工具來輔助,是一個很不錯的主意。 很多朋友喜歡在項目中以記錄日誌的方式來監控項目的流轉情況,其實 CoreCL ...
  • 本來閑來無事,準備看看Dapper擴展的源碼學習學習其中的編程思想,同時整理一下自己代碼的單元測試,為以後的進一步改進打下基礎。 突然就發現問題了,源碼也不看了,開始改代碼,改了好久。 測試Dapper.LiteSql數據批量插入的時候,耗時20秒,感覺不正常,於是我測試了非Dapper版的Lite ...
  • 需求如下,在DEV框架項目中,需要在表格中增加一列顯示圖片,並且能編輯該列圖片,然後進行保存等操作,最終效果如下 這裡使用的是PictureEdit控制項來實現,打開DEV GridControl設計器,在ColumnEdit選擇PictureEdit: 綁定圖片代碼如下: DataTable dtO ...
  • 前兩天微軟偷偷更新了Visual Studio 2022 正式版版本 17.3 發佈,發佈摘要: MAUI 工作負荷 GA 生成 MAUI/Blazor CSS 熱重載支持 現在,你將能夠使用我們的新增功能在 Visual Studio 中使用每個更新試用一系列新功能。 選擇每個功能以瞭解有關特定功 ...
  • 航天和軍工領域的數字化轉型和建設正在積極推進,在與航天二院、航天三院、航天六院、航天九院、無線電廠、兵工廠等單位交流的過程中,用戶更聚焦試驗和生產過程中的痛點,迫切需要解決軟體平臺統一監測和控制設備及軟體與設備協同的問題。 ...
  • .NET 項目預設情況下 日誌是使用的 ILogger 介面,預設提供一下四種日誌記錄程式: 控制台 調試 EventSource EventLog 這四種記錄程式都是預設包含在 .NET 運行時庫中。關於這四種記錄程式的詳細介紹可以直接查看微軟的官方文檔 https://docs.microsof ...
  • 一:背景 上一篇我們聊到瞭如何去找 熱點函數,這一篇我們來看下當你的程式出現了 非托管記憶體泄漏 時如何去尋找可疑的代碼源頭,其實思路很簡單,就是在 HeapAlloc 或者 VirtualAlloc 時做 Hook 攔截,記錄它的調用棧以及分配的記憶體量, PerfView 會將這個 分配量 做成一個 ...
  • 背景 在 CI/CD 流程當中,測試是 CI 中很重要的部分。跟開發人員關係最大的就是單元測試,單元測試編寫完成之後,我們可以使用 IDE 或者 dot cover 等工具獲得單元測試對於業務代碼的覆蓋率。不過我們需要一個獨立的 CLI 工具,這樣我們才能夠在 Jenkins 的 CI 流程集成。 ...
  • 一、應用場景 大家在使用Mybatis進行開發的時候,經常會遇到一種情況:按照月份month將數據放在不同的表裡面,查詢數據的時候需要跟不同的月份month去查詢不同的表。 但是我們都知道,Mybatis是ORM持久層框架,即:實體關係映射,實體Object與資料庫表之間是存在一一對應的映射關係。比 ...
  • 我國目前並未出台專門針對網路爬蟲技術的法律規範,但在司法實踐中,相關判決已屢見不鮮,K 哥特設了“K哥爬蟲普法”專欄,本欄目通過對真實案例的分析,旨在提高廣大爬蟲工程師的法律意識,知曉如何合法合規利用爬蟲技術,警鐘長鳴,做一個守法、護法、有原則的技術人員。 案情介紹 深圳市快鴿互聯網科技有限公司 2 ...