欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android多返回棧技術(shù)

 更新時(shí)間:2021年08月24日 14:16:42   作者:Andriod開(kāi)發(fā)者  
本文將詳情講解用戶(hù)通過(guò)系統(tǒng)返回按鈕導(dǎo)航回去的一組頁(yè)面,在開(kāi)發(fā)中被稱(chēng)為返回棧 (back stack)。多返回棧即一堆 "返回棧",對(duì)多返回棧的支持是在 Navigation 2.4.0-alpha01 和 Fragment 1.4.0-alpha01 中開(kāi)始的,有興趣的話(huà)一起參與學(xué)習(xí)

戶(hù)通過(guò)系統(tǒng)返回按鈕導(dǎo)航回去的一組頁(yè)面,在開(kāi)發(fā)中被稱(chēng)為返回棧 (back stack)。多返回棧即一堆 "返回棧",對(duì)多返回棧的支持是在 Navigation 2.4.0-alpha01 和 Fragment 1.4.0-alpha01 中開(kāi)始的。本文將為您展開(kāi)多返回棧的技術(shù)詳解。

1、系統(tǒng)返回按鈕的樂(lè)趣

無(wú)論您在使用 Android 全新的 手勢(shì)導(dǎo)航 還是傳統(tǒng)的導(dǎo)航欄,用戶(hù)的 "返回" 操作是 Android 用戶(hù)體驗(yàn)中關(guān)鍵的一環(huán),把握好返回功能的設(shè)計(jì)可以使應(yīng)用更加貼近整個(gè)生態(tài)系統(tǒng)。

在最簡(jiǎn)單的應(yīng)用場(chǎng)景中,系統(tǒng)返回按鈕僅僅 finish 您的 Activity。在過(guò)去您可能需要覆寫(xiě) Activity 的 onBackPressed() 方法來(lái)自定義返回操作,而在 2021 年您無(wú)需再這樣操作。我們已經(jīng)在OnBackPressedDispatcher 提供了針對(duì)自定義返回導(dǎo)航的API。實(shí)際上這與 FragmentManager 和 NavController 中 已經(jīng) 添加的 API 相同。

這意味著當(dāng)您使用 Fragments 或 Navigation 時(shí),它們會(huì)通過(guò) OnBackPressedDispatcher 來(lái)確保您調(diào)用了它們返回棧的 API,系統(tǒng)的返回按鈕會(huì)將您推入返回棧的頁(yè)面逐層返回。

多返回棧不會(huì)改變這個(gè)基本邏輯。系統(tǒng)的返回按鈕仍然是一個(gè)單向指令 —— "返回"。這對(duì)多返回棧 API 的實(shí)現(xiàn)機(jī)制有深遠(yuǎn)影響。

2、Fragment 中的多返回棧

在 surface 層級(jí),對(duì)于 多返回棧的支持 貌似很直接,但其實(shí)需要額外解釋一下 "Fragment 返回棧" 到底是什么。FragmentManager 的返回棧其實(shí)包含的不是 Fragment,而是由 Fragment 事務(wù)組成的。更準(zhǔn)確地說(shuō),是由那些調(diào)用了 addToBackStack(String name)) API 的事務(wù)組成的。

這就意味著當(dāng)您調(diào)用 commit() 提交了一個(gè)調(diào)用過(guò) addToBackStack() 方法的 Fragment 事務(wù)時(shí),F(xiàn)ragmentManager 會(huì)執(zhí)行所有您在事務(wù)中所指定的操作 (比如 替換操作),從而將每個(gè) Fragment 轉(zhuǎn)換為預(yù)期的狀態(tài)。然后 FragmentManager 會(huì)將該事務(wù)作為它返回棧的一部分。

當(dāng)您調(diào)用 popBackStack() 方法時(shí) (無(wú)論是直接調(diào)用,還是通過(guò)系統(tǒng)返回鍵以 FragmentManager 內(nèi)部機(jī)制調(diào)用),F(xiàn)ragment 返回棧的最上層事務(wù)會(huì)從棧中彈出 -- 比如新添加的 Fragment 會(huì)被移除,隱藏的 Fragment 會(huì)顯示。這會(huì)使得 FragmentManager 恢復(fù)到最初提交 Fragment 事務(wù)之前的狀態(tài)。

注: 這里有一個(gè)非常重要的事情需要大家注意,在同一個(gè) FragmentManager 中絕對(duì)不應(yīng)該將含有 addToBackStack() 的事務(wù)和不含的事務(wù)混在一起: 返回棧的事務(wù)無(wú)法察覺(jué)返回棧之外的 Fragment 事務(wù)的修改 —— 當(dāng)您從堆棧彈出一個(gè)非常不確定的元素時(shí),這些事務(wù)從下層替換出來(lái)的時(shí)候會(huì)撤銷(xiāo)之前未添加到返回棧的修改。
也就是說(shuō) popBackStack() 變成了銷(xiāo)毀操作: 任何已添加的 Fragment 在事務(wù)被彈出的時(shí)候都會(huì)丟失它的狀態(tài)。換言之,您會(huì)失去視圖的狀態(tài),任何所保存的實(shí)例狀態(tài) (Saved Instance State),并且任何綁定到該 Fragment 的 ViewModel 實(shí)例都會(huì)被清除。這也是該 API 和新的 saveBackStack() 方法之間的主要區(qū)別。saveBackStack() 可以實(shí)現(xiàn)彈出事務(wù)所實(shí)現(xiàn)的返回效果,此外它還可以確保視圖狀態(tài)、已保存的實(shí)例狀態(tài),以及 ViewModel 實(shí)例能夠在銷(xiāo)毀時(shí)被保存。這使得 restoreBackStack() API 后續(xù)可以通過(guò)已保存的狀態(tài)重建這些事務(wù)和它們的 Fragment,并且高效 "重現(xiàn)" 已保存的全部細(xì)節(jié)。太神奇了!

而實(shí)現(xiàn)這個(gè)目的必須要解決大量技術(shù)上的問(wèn)題。

3、排除 Fragment 在技術(shù)上的障礙

雖然 Fragment 總是會(huì)保存 Fragment 的視圖狀態(tài),但是 Fragment 的 onSaveInstanceState() 方法只有在 Activity 的 onSaveInstanceState() 被調(diào)用時(shí)才會(huì)被調(diào)用。為了能夠保證調(diào)用 saveBackStack() 時(shí) SavedInstanceState 會(huì)被保存,我們 還 需要在 Fragment 生命周期切換 的正確時(shí)機(jī)注入對(duì) onSaveInstanceState() 的調(diào)用。我們不能調(diào)用得太早 (您的 Fragment 不應(yīng)該在 STARTED 狀態(tài)下保存狀態(tài)),也不能調(diào)用得太晚 (您需要在 Fragment 被銷(xiāo)毀之前保存狀態(tài))。

這樣的前提條件就開(kāi)啟了需要 解決 FragmentManager 轉(zhuǎn)換到對(duì)應(yīng)狀態(tài)的問(wèn)題,以此來(lái)保障有一個(gè)地方能夠?qū)?Fragment 轉(zhuǎn)換為所需狀態(tài),并且處理可重入行為和 Fragment 內(nèi)部的狀態(tài)轉(zhuǎn)換。

在 Fragment 的重構(gòu)工作進(jìn)行了 6 個(gè)月,進(jìn)行了 35 次修改時(shí),發(fā)現(xiàn) Postponed Fragment 功能已經(jīng)嚴(yán)重?fù)p壞,這一問(wèn)題使得被推遲的事務(wù)處于一個(gè)中間狀態(tài) —— 既沒(méi)有被提交也并不是未被提交。之后的 65 個(gè)修改和 5 個(gè)月的時(shí)間里,我們幾乎重寫(xiě)了 FragmentManager 管理狀態(tài)、延遲狀態(tài)切換和動(dòng)畫(huà)的內(nèi)部代碼,具體請(qǐng)參見(jiàn)我們之前的文章《全新的 Fragment: 使用新的狀態(tài)管理器》。

4、Fragment 中值得期待的地方

隨著技術(shù)問(wèn)題的逐步解決,包括更加可靠和更易理解的 FragmentManager,我們新增加了兩個(gè) API: saveBackStack() 和 restoreBackStack()。

如果您不使用這些新增 API,則一切照舊: 單個(gè) FragmentManager 返回棧和之前的功能相同?,F(xiàn)有的 addToBackStack() 保持不變 —— 您可以將 name 賦值為 null 或者任意 name。然而,當(dāng)您使用多返回棧時(shí),name 的作用就非常重要了: 在您調(diào)用 saveBackStack() 和之后的 restoreBackStack() 方法時(shí),它將作為 Fragment 事務(wù)的唯一的 key。

舉個(gè)例子,會(huì)更容易理解。比如您已經(jīng)添加了一個(gè)初始的 Fragment 到 Activity,然后提交了兩個(gè)事務(wù),每個(gè)事務(wù)中包含一個(gè)單獨(dú)的 replace 操作:

// 這是用戶(hù)看到的初始的 Fragment
fragmentManager.commit {
 setReorderingAllowed(true)
 replace<HomeFragment>(R.id.fragment_container)
}
// 然后,響應(yīng)用戶(hù)操作,我們?cè)诜祷貤V性黾恿藘蓚€(gè)事務(wù)
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”)
}

也就是說(shuō)我們的 FragmentManager 會(huì)變成這樣:

△ 提交三次之后的 FragmentManager 的狀態(tài)

比如說(shuō)我們希望將 profile 頁(yè)換出返回棧,然后切換到通知 Fragment。這就需要調(diào)用 saveBackStack() 并且緊跟一個(gè)新的事務(wù):

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

現(xiàn)在我們添加 ProfileFragment 的事務(wù)和添加 EditProfileFragment 的事務(wù)都保存在 "profile" 關(guān)鍵字下。這些 Fragment 已經(jīng)完全將狀態(tài)保存,并且 FragmentManager 會(huì)隨同事務(wù)狀態(tài)一起保持它們的狀態(tài)。很重要的一點(diǎn): 這些 Fragment 的實(shí)例并不在內(nèi)存中或者在 FragmentManager 中 —— 存在的僅僅只有狀態(tài) (以及任何以 ViewModel 實(shí)例形式存在的非配置狀態(tài))。


△ 我們保存 profile 返回棧并且添加一個(gè)新的 commit 后的 FragmentManager 狀態(tài)

替換回來(lái)非常簡(jiǎn)單: 我們可以在 "notifications" 事務(wù)中同樣調(diào)用 saveBackStack() 操作,然后調(diào)用 restoreBackStack():

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

這兩個(gè)堆棧項(xiàng)高效地交換了位置:

△ 交換堆棧項(xiàng)后的 FragmentManager 狀態(tài)

維持一個(gè)單獨(dú)且活躍的返回棧并且將事務(wù)在其中交換,這保證了當(dāng)返回按鈕被點(diǎn)擊時(shí),F(xiàn)ragmentManager 和系統(tǒng)的其他部分可以保持一致的響應(yīng)。實(shí)際上,整個(gè)邏輯并未改變,同之前一樣,仍然彈出 Fragment 返回棧的最后一個(gè)事務(wù)。

這些 API 都特意按照最小化設(shè)計(jì),盡管它們會(huì)產(chǎn)生潛在的影響。這使得開(kāi)發(fā)者可以基于這些接口設(shè)計(jì)自己的結(jié)構(gòu),而無(wú)需通過(guò)任何非常規(guī)的方式保存 Fragment 的視圖狀態(tài)、已保存的實(shí)例狀態(tài)、非配置的狀態(tài)。

當(dāng)然了,如果您不希望在這些 API 之上構(gòu)建您的框架,那么可以使用我們所提供的框架進(jìn)行開(kāi)發(fā)。

4、使用 Navigation 將多返回棧適配到任意屏幕類(lèi)型

Navigation Component 最初 是作為通用運(yùn)行時(shí)組件進(jìn)行開(kāi)發(fā)的,其中不涉及 View、Fragment、Composable 或者其他屏幕顯示相關(guān)類(lèi)型及您可能會(huì)在 Activity 中實(shí)現(xiàn)的 "目的地界面"。然而,NavHost 接口 的實(shí)現(xiàn)中需要考慮這些內(nèi)容,通過(guò)它添加一個(gè)或者多個(gè) Navigator 實(shí)例時(shí),這些實(shí)例 確實(shí) 清楚如何與特定類(lèi)型的目的地進(jìn)行交互。

這也就意味著與 Fragment 的交互邏輯全部封裝在了 navigation-fragment 開(kāi)發(fā)庫(kù)和它其中的 FragmentNavigator 與 DialogFragmentNavigator 中。類(lèi)似的,與 Composable 的交互邏輯被封裝在完全獨(dú)立的 navigation-compose 開(kāi)發(fā)庫(kù)和它的 ComposeNavigator 中。這里的抽象設(shè)計(jì)意味著如果您希望僅僅通過(guò) Composable 構(gòu)建您的應(yīng)用,那么當(dāng)您使用 Navigation Compose 時(shí)無(wú)需任何涉及到 Fragment 的依賴(lài)。

該級(jí)別的分離意味著 Navigation 中有兩個(gè)層次來(lái)實(shí)現(xiàn)多返回棧:

  • 保存獨(dú)立的 NavBackStackEntry 實(shí)例狀態(tài),這些實(shí)例組成了 NavController 返回棧。這是屬于 NavController 的職責(zé)。
  • 保存 Navigator 針對(duì)每個(gè) NavBackStackEntry 的特定狀態(tài) (比如與 FragmentNavigator 目的地相關(guān)聯(lián)的 Fragment)。這是屬于 Navigator 的職責(zé)。

仍需特別注意那些 尚未 更新的 Navigator,它們無(wú)法支持保存自身狀態(tài)。底層的 Navigator API 已經(jīng)整體重寫(xiě)來(lái)支持狀態(tài)保存 (您需要覆寫(xiě)新增的 navigate() 和 popBackStack() API 的重載方法,而不是覆寫(xiě)之前的版本),即使 Navigator 并未更新,NavController 仍會(huì)保存 NavBackStackEntry 的狀態(tài) (在 Jetpack 世界中向后兼容是非常重要的)。

備注: 通過(guò)綁定 TestNavigatorState 使其成為一個(gè) mini-NavController 可以實(shí)現(xiàn)在新的 Navigator API 上更輕松、獨(dú)立地測(cè)試您自定義的 Navigator。
如果您僅僅在應(yīng)用中使用 Navigation,那么 Navigator 這個(gè)層面更多的是實(shí)現(xiàn)細(xì)節(jié),而不是您需要直接與之交互的內(nèi)容??梢赃@么說(shuō),我們已經(jīng)完成了將 FragmentNavigator 和 ComposeNavigator 遷移到新的 Navigator API 的工作,使其能夠正確地保存和恢復(fù)它們的狀態(tài),在這個(gè)層面上您無(wú)需再做任何額外工作。

5、在 Navigation 中啟用多返回棧

如果您正在使用 NavigationUI,它是用于連接您的 NavController 到 Material 視圖組件的一系列專(zhuān)用助手,您會(huì)發(fā)現(xiàn)對(duì)于菜單項(xiàng)、BottomNavigationView (現(xiàn)在叫 NavigationRailView) 和 NavigationView,多返回棧是 默認(rèn)啟用 的。這就意味著結(jié)合 navigation-fragment 和 navigation-ui 使用就可以。

NavigationUI API 是基于 Navigation 的其他公共 API 構(gòu)建的,確保您可以準(zhǔn)確地為自定義組件構(gòu)建您自己的版本。保證您可以構(gòu)建所需的自定義組件。啟用保存和恢復(fù)返回棧的 API 也不例外,在 Navigation XML 中通過(guò) NavOptions 上的新 API,也就是 navOptions Kotlin DSL,以及 popBackStack() 的重載方法可以幫助您指定 pop 操作保存狀態(tài)或者指定 navigate 操作來(lái)恢復(fù)之前已保存的狀態(tài)。

比如,在 Compose 中,任何全局的導(dǎo)航模式 (無(wú)論是底部導(dǎo)航欄、導(dǎo)航邊欄、抽屜式導(dǎo)航欄或者任何您能想到的形式) 都可以使用我們?cè)谂c 底部導(dǎo)航欄集成 所介紹的相同的技術(shù),并且結(jié)合 saveState 和 restoreState 屬性一起調(diào)用 navigate():

onClick = {
 navController.navigate(screen.route) {
   // 當(dāng)用戶(hù)選擇子項(xiàng)時(shí)在返回棧中彈出到導(dǎo)航圖中的起始目的地
   // 來(lái)避免太過(guò)臃腫的目的地堆棧
   popUpTo(navController.graph.findStartDestination().id) {
     saveState = true
   }

   // 當(dāng)重復(fù)選擇相同項(xiàng)時(shí)避免相同目的地的多重拷貝
   launchSingleTop = true
   // 當(dāng)重復(fù)選擇之前已經(jīng)選擇的項(xiàng)時(shí)恢復(fù)狀態(tài)
   restoreState = true
 }
}

5、保存狀態(tài),鎖定用戶(hù)

對(duì)用戶(hù)來(lái)說(shuō),最令人沮喪的事情之一便是丟失之前的狀態(tài)。這也是為什么 Fragment 用一整頁(yè)來(lái)講解 保存與 Fragment 相關(guān)的狀態(tài),而且也是我非常樂(lè)于更新每個(gè)層級(jí)來(lái)支持多返回棧的原因之一:

  • Fragments (比如完全不使用 Navigation Component): 通過(guò)使用新的 FragmentManager API,也就是 saveBackStack 和 restoreBackStack。
  • 核心的 Navigation 運(yùn)行時(shí): 添加可選的新的 NavOptions 方法用于 restoreState(恢復(fù)狀態(tài)) 和 saveState (保存狀態(tài)) 以及新的 popBackStack() 的重載方法,它同樣可以傳入一個(gè)布爾型的 saveState 參數(shù) (默認(rèn)是 false)。
  • 通過(guò) Fragment 實(shí)現(xiàn) Navigation: FragmentNavigator 現(xiàn)在利用新的 NavigatorAPI,通過(guò)使用 Navigation 運(yùn)行時(shí) API 將 Navigation 運(yùn)行時(shí) API 轉(zhuǎn)換為 Fragment API。
  • NavigationUI: 每當(dāng)它們彈出返回棧時(shí),onNavDestinationSelected()、NavigationBarView.setupWithNavController() 和 NavigationView.setupWithNavController() 現(xiàn)在默認(rèn)使用 restoreState 和 saveState 這兩個(gè)新的 NavOption。也就意味著 當(dāng)升級(jí)到 Navigation 2.4.0-alpha01 或者更高版本后,任何使用 NavigationUI API 的應(yīng)用無(wú)需修改代碼即可實(shí)現(xiàn)多返回棧。

到此這篇關(guān)于Android多返回棧技術(shù)的文章就介紹到這了,更多相關(guān)Android多返回棧內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論