日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

activity 点击后传递数据给fragment_Fragment 新特性 : Fragment Result API 使用以及源码分析

發布時間:2023/12/10 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 activity 点击后传递数据给fragment_Fragment 新特性 : Fragment Result API 使用以及源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  • 原標題: Android Fragments: Fragment Result
  • 原文地址: https://proandroiddev.com/android-fragments-fragment-result......
  • 原文作者: Husayn Hakeem

今年 Google 推出了 Fragment Result API 和 Activity Results API,用來取代之前的 Activity 和 Fragment 之間通信方式的不足。

這篇文章大概是我在 5 月份的寫的,主要介紹 Fragment Result API,分為 譯文 和 譯者的思考 兩個部分。

Fragment Result API 主要介紹 Fragment 間通信的新方式,是在 Fragment 1.3.0-alpha04 新增加的 API ,而現在最新版本已經到 fragment-1.3.0-beta01 應該很快就能應用在項目里面了。

接下來分析一下 Fragment Result API 主要為我們解決了什么問題,它都有那些更新。

通過這篇文章你將學習到以下內容,將在譯者思考部分會給出相應的答案

  • 新 Fragment 間通信的方式的使用?
  • 新 Fragment 間通信的源碼分析?
  • 匯總 Fragment 之間的通信的方式?

譯文

Frrgament 間傳遞數據可以通過多種方式,包括使用 target Fragment APIs (Fragment.setTargetFragment() 和 Fragment.getTargetFragment()),ViewModel 或者 使用 Fragments’ 父容器 Activity,target Fragment APIs 已經過時了,現在鼓勵使用新的 Fragment result APIs 完成 Frrgament 之間傳遞數據,其中傳遞數據由 FragmentManager 處理,并且在 Fragments 設置發送數據和接受數據

在 Frrgament 之間傳遞數據

使用新的 Fragment APIs 在 兩個 Frrgament 之間的傳遞,沒有任何引用,可以使用它們公共的 FragmentManager,它充當 Frrgament 之間傳遞數據的中心存儲。

接受數據

如果想在 Fragment 中接受數據,可以在 FragmentManager 中注冊一個 FragmentResultListener,參數 requestKey 可以過濾掉 FragmentManager 發送的數據

FragmentManager.setFragmentResultListener(requestKey,lifecycleOwner,FragmentResultListener { requestKey: String, result: Bundle ->// Handle result})

參數 lifecycleOwner 可以觀察生命周期,當 Fragment 的生命周期處于 STARTED 時接受數據。如果監聽 Fragment 的生命周期,您可以在接收到新數據時安全地更新 UI,因為 view 的創建(onViewCreated() 方法在 onStart() 之前被調用)。

當生命周期處于 LifecycleOwner STARTED 的狀態之前,如果有多個數據傳遞,只會接收到最新的值

當生命周期處于 LifecycleOwner DESTROYED 時,它將自動移除 listener,如果想手動移除 listener,需要調用 FragmentManager.setFragmentResultListener() 方法,傳遞空的 FragmentResultListener

在 FragmentManager 中注冊 listener,依賴于 Fragment 發送返回的數據

  • 如果在 FragmentA 中接受 FragmentB 發送的數據,FragmentA 和 FragmentB 處于相同的層級,通過 parent FragmentManager 進行通信,FragmentA 必須使用 parent FragmentManager 注冊 listener
parentFragmentManager.setFragmentResultListener(...)
  • 如果在 FragmentA 中接受 FragmentB 發送的數據,FragmentA 是 FragmentB 的父容器, 他們通過 child FragmentManager 進行通信
childFragmentManager.setFragmentResultListener(...)

listener 必須設置的Fragment 相同的 FragmentManager

發送數據

如果 FragmentB 發送數據給 FragmentA,需要在 FragmentA 中注冊 listener,通過 parent FragmentManager 發送數據

parentFragmentManager.setFragmentResult(requestKey, // Same request key FragmentA used to register its listenerbundleOf(key to value) // The data to be passed to FragmentA )

測試 Fragment Results

測試 Fragment 是否成功接收或發送數據,可以使用 FragmentScenario API

接受數據

如果在 FragmentA 中注冊 FragmentResultListener 接受數據,你可以模擬 parent FragmentManager 發送數據,如果在 FragmentA 中正確注冊了 listener,可以用來驗證 FragmentA 是否能收到數據,例如,如果在 FragmentA 中接受數據并更新 UI, 可以使用 Espresso APIs 來驗證是否期望的數據

@Test fun shouldReceiveData() {val scenario = FragmentScenario.launchInContainer(FragmentA::class.java)// Pass data using the parent fragment managerscenario.onFragment { fragment ->val data = bundleOf(KEY_DATA to "value")fragment.parentFragmentManager.setFragmentResult("aKey", data)}// Verify data is received, for example, by verifying it's been displayed on the UIonView(withId(R.id.textView)).check(matches(withText("value"))) }

發送數據

可以在 FragmentB 的 parent FragmentManager 上注冊一個 FragmentResultListener 來測試 FragmentB 是否成功發送數據,當發送數據結束時,可以來驗證這個 listener 是否能收到數據

@Test fun shouldSendData() {val scenario = FragmentScenario.launchInContainer(FragmentB::class.java)// Register result listenervar receivedData = ""scenario.onFragment { fragment ->fragment.parentFragmentManager.setFragmentResultListener(KEY,fragment,FragmentResultListener { key, result ->receivedData = result.getString(KEY_DATA)})}// Send dataonView(withId(R.id.send_data)).perform(click())// Verify data was successfully sentassertThat(receivedData).isEqualTo("value") }

總結

雖然使用了 Fragment result APIs,替換了過時的 Fragment target APIs,但是新的 APIs 在Bundle 作為數據傳傳遞方面有一些限制,只能傳遞簡單數據類型、Serializable 和 Parcelable 數據,Fragment result APIs 允許程序從崩潰中恢復數據,而且不會持有對方的引用,避免當 Fragment 處于不可預知狀態的時,可能發生未知的問題

譯者的思考

這是譯者的一些思考,總結一下 Fragment 1.3.0-alpha04 新增加的 Fragment 間通信的 API

數據接受

FragmentManager.setFragmentResultListener(requestKey,lifecycleOwner,FragmentResultListener { requestKey: String, result: Bundle ->// Handle result})

數據發送

parentFragmentManager.setFragmentResult(requestKey, // Same request key FragmentA used to register its listenerbundleOf(key to value) // The data to be passed to FragmentA )

那么 Fragment 間通信的新 API 給我們帶來哪些好處呢:

  • 在 Fragment 之間傳遞數據,不會持有對方的引用
  • 當生命周期處于 ON_START 時開始處理數據,避免當 Fragment 處于不可預知狀態的時,可能發生未知的問題
  • 當生命周期處于 ON_DESTROY 時,移除監聽

我們一起來從源碼的角度分析一下 Google 是如何做的

源碼分析

按照慣例從調用的方法來分析,數據接受時,調用了 FragmentManager 的 setFragmentResultListener 方法

androidx.fragment/fragment/1.3.0-alpha04......androidx/fragment/app/FragmentManager.java

private final ConcurrentHashMap<String, LifecycleAwareResultListener> mResultListeners =new ConcurrentHashMap<>();@Override public final void setFragmentResultListener(@NonNull final String requestKey,@NonNull final LifecycleOwner lifecycleOwner,@Nullable final FragmentResultListener listener) {// mResultListeners 是 ConcurrentHashMap 的實例,用來儲存注冊的 listener// 如果傳遞的參數 listener 為空時,移除 requestKey 對應的 listenerif (listener == null) {mResultListeners.remove(requestKey);return;}// Lifecycle是一個生命周期感知組件,一般用來響應Activity、Fragment等組件的生命周期變化final Lifecycle lifecycle = lifecycleOwner.getLifecycle();// 當生命周期處于 DESTROYED 時,直接返回// 避免當 Fragment 處于不可預知狀態的時,可能發生未知的問題if (lifecycle.getCurrentState() == Lifecycle.State.DESTROYED) {return;}// 開始監聽生命周期LifecycleEventObserver observer = new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {// 當生命周期處于 ON_START 時開始處理數據if (event == Lifecycle.Event.ON_START) {// 開始檢查受到的數據Bundle storedResult = mResults.get(requestKey);if (storedResult != null) {// 如果結果不為空,調用回調方法listener.onFragmentResult(requestKey, storedResult);// 清除數據setFragmentResult(requestKey, null);}}// 當生命周期處于 ON_DESTROY 時,移除監聽if (event == Lifecycle.Event.ON_DESTROY) {lifecycle.removeObserver(this);mResultListeners.remove(requestKey);}}};lifecycle.addObserver(observer);mResultListeners.put(requestKey, new FragmentManager.LifecycleAwareResultListener(lifecycle, listener)); }
  • Lifecycle是一個生命周期感知組件,一般用來響應Activity、Fragment等組件的生命周期變化
  • 獲取 Lifecycle 去監聽 Fragment 的生命周期的變化
  • 當生命周期處于 ON_START 時開始處理數據,避免當 Fragment 處于不可預知狀態的時,可能發生未知的問題
  • 當生命周期處于 ON_DESTROY 時,移除監聽

接下來一起來看一下數據發送的方法,調用了 FragmentManager 的 setFragmentResult 方法

androidx.fragment/fragment/1.3.0-alpha04......androidx/fragment/app/FragmentManager.java

private final ConcurrentHashMap<String, Bundle> mResults = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, LifecycleAwareResultListener> mResultListeners =new ConcurrentHashMap<>();@Override public final void setFragmentResult(@NonNull String requestKey, @Nullable Bundle result) {if (result == null) {// mResults 是 ConcurrentHashMap 的實例,用來存儲數據傳輸的 Bundle// 如果傳遞的參數 result 為空,移除 requestKey 對應的 BundlemResults.remove(requestKey);return;}// mResultListeners 是 ConcurrentHashMap 的實例,用來儲存注冊的 listener// 獲取 requestKey 對應的 listenerLifecycleAwareResultListener resultListener = mResultListeners.get(requestKey);if (resultListener != null && resultListener.isAtLeast(Lifecycle.State.STARTED)) {// 如果 resultListener 不為空,并且生命周期處于 STARTED 狀態時,調用回調resultListener.onFragmentResult(requestKey, result);} else {// 否則保存當前傳輸的數據mResults.put(requestKey, result);} }
  • 獲取 requestKey 注冊的 listener
  • 當生命周期處于 STARTED 狀態時,開始發送數據
  • 否則保存當前傳輸的數據

源碼分析到這里結束了,我們一起來思考一下,在之前我們的都有那些數據傳方式

匯總 Fragment 之間的通信的方式

  • 通過共享 ViewModel 或者關聯 Activity來完成,Fragment 之間不應該直接通信 參考 Google: ViewModel#sharing
  • 通過接口,可以在 Fragment 定義接口,并在 Activity 實現它 參考 Google: 與其他 Fragment 通信
  • 通過使用 findFragmentById 方法,獲取 Fragment 的實例,然后調用 Fragment 的公共方法 參考 Google: 與其他 Fragment 通信
  • 調用 Fragment.setTargetFragment() 和 Fragment.getTargetFragment() 方法,但是注意 target fragment 需要直接訪問另一個 fragment 的實例,這是十分危險的,因為你不知道目標 fragment 處于什么狀態
  • Fragment 新的 API, setFragmentResult() 和 setFragmentResultListener()

綜合以上通信方式,那么你認為 Fragment 之間通信最好的方式是什么?

參考文獻

  • Now in Android #17: https://medium.com/androiddeve......
  • Pass data between fragments: https://developer.android.com/training/basi......
  • ViewModel#sharing: https://developer.android.com/topic/librari......
  • 與其他 Fragment 通信: https://developer.android.com/training/basic......

結語

全文到這里就結束了,如果有幫助 點個贊 就是對我最大的鼓勵!

致力于分享一系列 Android 系統源碼、逆向分析、算法、翻譯、Jetpack 源碼相關的文章,在技術的道路上一起前進


最后推薦我一直在更新維護的項目和網站:

  • 計劃建立一個最全、最新的 AndroidX Jetpack 相關組件的實戰項目 以及 相關組件原理分析文章,正在逐漸增加 Jetpack 新成員,倉庫持續更新,歡迎前去查看:
AndroidX-Jetpack-Practice?github.com
  • LeetCode / 劍指 offer / 國內外大廠面試題 / 多線程 題解,語言 Java 和 kotlin,包含多種解法、解題思路、時間復雜度、空間復雜度分析

    • 劍指 offer 及國內外大廠面試題解:
劍指Offer題解?offer.hi-dhl.com
    • LeetCode 系列題解:
LeetCode 系列題解?leetcode.hi-dhl.com
  • 最新 Android 10 源碼分析系列文章,了解系統源碼,不僅有助于分析問題,在面試過程中,對我們也是非常有幫助的,倉庫持續更新,歡迎前去查看
hi-dhl/Android10-Source-Analysis?github.com
  • 整理和翻譯一系列精選國外的技術文章,每篇文章都會有譯者思考部分,對原文的更加深入的解讀,倉庫持續更新,歡迎前去查看
hi-dhl/Technical-Article-Translation?github.com
  • 「為互聯網人而設計,國內國外名站導航」涵括新聞、體育、生活、娛樂、設計、產品、運營、前端開發、Android 開發等等網址,歡迎前去查看
Hi World | 為互聯網人而設計的國內國外名站導航?site.51git.cn

總結

以上是生活随笔為你收集整理的activity 点击后传递数据给fragment_Fragment 新特性 : Fragment Result API 使用以及源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。