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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

全新的 Fragment 通信方式

發布時間:2024/8/23 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 全新的 Fragment 通信方式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者?|?tech-bus.丹卿

來源 | 程序員巴士

前言

就在前段時間,Google 推出了 Fragment Result API 和 Activity Results API,用來取代之前的 Activity 和 Fragment 之間通信方式的不足,大家可以前往看看都有哪些更新:

https://medium.com/androiddevelopers/now-in-android-17-9d73f7bed7f

通過Fragment Result API進行Fragment間數據傳遞:

發送數據

@Override @NonNull public?final?@Override?void?setFragmentResult(@NonNull?String?requestKey,?@NonNull?Bundle?result)

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

數據接收:

@Override @NonNull public?final?@Override?void?setFragmentResultListener(@NonNull?String?requestKey,@NonNull?LifecycleOwner?lifecycleOwner,@NonNull?FragmentResultListener?listener )

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

setFragmentResultListener為給定的requestKey設置了ResultListener。一旦給定的 LifecycleOwner 至少處于 STARTED 狀態, setFragmentResult 使用相同的 requestKey 設置的任何結果都將傳遞給回調。回調將保持活動狀態,直到 LifecycleOwner 達到 DESTROYED 狀態或使用相同的 requestKey 調用 clearFragmentResultListener。

時序圖分析:

可以通過簡化后的時序圖來分析lifecycle狀態和fragment設置監聽的順序:

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

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

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

從時序圖很明顯可以總體分析出流程來,大致是在FragmentManager中注冊listener,依賴于Fragment發送返回的數據。

不同層級關系的Fragment數據傳遞

一般fragment數據傳遞涉及到不同層級間的傳遞,主要分為下面兩種:

父子層級的兩個Fragment數據傳遞

如果在 FragmentA 中接受 FragmentB 發送的數據,FragmentA 是 FragmentB 的父容器, 他們通過 child FragmentManager 進行通信

childFragmentManager.setFragmentResultListener(...)

注意 listener必須設置的Fragment需要用到相同的FragmentManager。

相同層級的兩個Fragment數據傳遞

如果在 FragmentA 中接受 FragmentB 發送的數據,FragmentA 和 FragmentB 處于相同的層級,通過 parent FragmentManager 進行通信,FragmentA 必須使用 parent FragmentManager 注冊 listener

parentFragmentManager.setFragmentResultListener(...)

源碼解析

不同于之前舊的Target Fragment Api,可以看到這里將監聽器和fragment的lifecycle進行綁定,這樣將帶來以下優點:

  • 在 Fragment 之間傳遞數據,不會持有對方的引用

  • 當生命周期處于 ON_START 時開始處理數據,避免當 Fragment 處于不可預知狀態的時,可能發生未知的問題

  • 當生命周期處于 ON_DESTROY 時,移除監聽

那讓我們更進一步看下,fragment和它的lifecycle是如何進行數據監聽的綁定和解綁的呢:

@Overridepublic?final?void?setFragmentResultListener(@NonNull?final?String?requestKey,@NonNull?final?LifecycleOwner?lifecycleOwner,@NonNull?final?FragmentResultListener?listener)?{final?Lifecycle?lifecycle?=?lifecycleOwner.getLifecycle();//destroyed則直接返回if?(lifecycle.getCurrentState()?==?Lifecycle.State.DESTROYED)?{return;}LifecycleEventObserver?observer?=?new?LifecycleEventObserver()?{@Overridepublic?void?onStateChanged(@NonNull?LifecycleOwner?source,@NonNull?Lifecycle.Event?event)?{//在start的時候進行方法調用if?(event?==?Lifecycle.Event.ON_START)?{//?一旦出于start狀態,檢查存儲結果Bundle?storedResult?=?mResults.get(requestKey);if?(storedResult?!=?null)?{//?如果查詢的結果不為空,則觸發回調listener.onFragmentResult(requestKey,?storedResult);//?清除結果clearFragmentResult(requestKey);}}//destroy則移除監聽if?(event?==?Lifecycle.Event.ON_DESTROY)?{lifecycle.removeObserver(this);mResultListeners.remove(requestKey);}}};

可以看到上面代碼做了:

  • 獲取 Lifecycle 去監聽 Fragment 的生命周期的變化

  • 當生命周期處于 ON_START 時開始處理數據,避免當 Fragment 處于不可預知狀態的時,可能發生未知的問題

  • 當生命周期處于 ON_DESTROY 時,移除監聽

  • 當生命周期處于 DESTROYED 則直接返回不作處理

看過接受數據如何做的,下面再看下如何發送數據的:

@Overridepublic?final?void?setFragmentResult(@NonNull?String?requestKey,?@NonNull?Bundle?result)?{//?檢查是否有監聽器去監聽requestkey結果FragmentManager.LifecycleAwareResultListener?resultListener?=?mResultListeners.get(requestKey);//?如果生命周期started,則觸發回調if?(resultListener?!=?null?&&?resultListener.isAtLeast(Lifecycle.State.STARTED))?{resultListener.onFragmentResult(requestKey,?result);}?else?{//否則?保存當前傳輸數據resultmResults.put(requestKey,?result);}}
  • 獲取 requestKey 注冊的 listener

  • 當生命周期處于 STARTED 狀態時,開始發送數據

  • 否則保存當前傳輸的數據

看完源碼簡單分享,那么再來看下fragment間通信還有哪些其他方法?

Fragment中的通信方式還有哪些

通過使用findFragmentById或關聯Activity獲取Fragment的實例,然后調用Fragment的公共方法:

  • 第一步在被調用的MainFragment注冊公共方法

//MainFragment.java文件中public?void?setData(List<String>?dataList)?{adapter.set(dataList);}
  • 第二步 在主動調用的Fragment中關聯activity并獲取到MainFragment后,調用公共方法

//MenuFragment.java文件中lv.setOnItemClickListener(new?AdapterView.OnItemClickListener()?{@Overridepublic?void?onItemClick(AdapterView<?>?parent,?View?view,?int?position,?long?id)?{MainFragment?mainFragment?=(MainFragment)?getActivity().getSupportFragmentManager().findFragmentByTag("mainFragment");mainFragment.setData(mDataList.get(position));}});
  • 缺點: Fragment 之間不應該直接通信 參考 https://developer.android.com/topic/libraries/architecture/viewmodel#sharing

接口回調的方式進行fragment間數據傳遞:

  • step1: 在Menuragment中創建一個接口以及接口對應的set方法:

//MainFragment.java文件中public?interface?OnDataTransferListener?{public?void?dataTransfer(List<String>?dataList);}public?void?setOnDataTransferListener(OnDataTransferListener?mListener)?{this.mListener?=?mListener;}
  • step2: 在MenuFragment中的ListView條目點擊事件中進行接口進行接口回調

//MenuFragment.java文件中lv.setOnItemClickListener(new?AdapterView.OnItemClickListener()?{@Overridepublic?void?onItemClick(AdapterView<?>?parent,?View?view,?int?position,?long?id)?{if?(mListener?!=?null)?{mListener.dataTransfer(mDataList.get(position));}}});
  • step3: 在MainActivity中根據menuFragment獲取到接口的set方法,在這個方法中進行進行數據傳遞,具體如下:

//在MainActivity中 menuFragment.setOnDataTransferListener(new?MenuFragment.OnDataTransferListener()?{@Overridepublic?void?dataTransfer(List<String>?data)?{mainFragment.setData(data);}});
  • 缺點: 相對Result API更復雜,具體可以參考官方文檔 https://developer.android.com/training/basics/fragments/communicating

通過Target Fragment APIs (Fragment.setTargetFragment() & Fragment.getTargetFragment())方法進行Fragment間數據傳遞:

援引Google官方的說明:

Fragment.setTargetFragment()

Use case = 2 fragments hosted by the same activity.

Where startActivityForResult() establishes a relationship between 2 activities, setTargetFragment() defines the caller/called relationship between 2 fragments.

setTargetFragment(target) lets the "called" fragment know where to send the result. onActivityResult() is called manually in this case.

通過下面的偽代碼可以表示出調用關系:

public?class?Caller?extends?FragmentFragment?called?=?Called.newInstance()called.setTargetFragment(this)public?class?Called?extends?DialogFragmentintent?=?amazingDatagetTargetFragment().onActivityResult(??getTargetRequestCode(),,intent??)

簡而言之,假設Fragment A 跳轉B ?在B中做一些操作之后,想把這些操作回傳給A Fragment中存在startActivityForResult()以及onActivityResult()方法,但沒有setResult()方法,用于設置返回的intent,這樣我們就需要通過調用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);

這種方法無形當中增加了兩個Fragment 與 ?Activity的耦合度 所以,就有了setTargetFragment()方法 在啟動B的時候,可以調用 B.setTargetFragment(A,int t) 將AB關聯起來, 然后在B的代碼中調用 getTargetFragment().onActivityResult()將數據回傳給 A

  • 缺點: 該方法目前已經被谷歌官方所廢棄,Target fragment 需要直接訪問另一個fragment 的實例,這是十分危險的,因為不知道目標fragment處于什么狀態;

通過ViewModel容器進行Fragment間數據傳遞:

Activity 中的兩個或更多 Fragment 需要相互通信是一種很常見的現象。想象一下拆分視圖 (list-detail) Fragment 的常見情況,假設您有一個 Fragment,在該 Fragment 中,用戶從列表中選擇一項,還有另一個 Fragment,用于顯示選定項的內容。這種情況不太容易處理,因為這兩個 Fragment 都需要定義某種接口描述,并且所有者 Activity 必須將兩者綁定在一起。此外,這兩個 Fragment 都必須處理另一個 Fragment 尚未創建或不可見的情況。

可以使用 ViewModel 對象解決這一常見的難點。這兩個 fragment 可以使用其 activity 范圍共享 ViewModel 來處理此類通信,如以下示例代碼所示:

public?class?SharedViewModel?extends?ViewModel?{private?final?MutableLiveData<Item>?selected?=?new?MutableLiveData<Item>();public?void?select(Item?item)?{selected.setValue(item);}public?LiveData<Item>?getSelected()?{return?selected;} }public?class?ListFragment?extends?Fragment?{private?SharedViewModel?model;public?void?onViewCreated(@NonNull?View?view,?Bundle?savedInstanceState)?{super.onViewCreated(view,?savedInstanceState);model?=?new?ViewModelProvider(requireActivity()).get(SharedViewModel.class);itemSelector.setOnClickListener(item?->?{model.select(item);});} }public?class?DetailFragment?extends?Fragment?{public?void?onViewCreated(@NonNull?View?view,?Bundle?savedInstanceState)?{super.onViewCreated(view,?savedInstanceState);SharedViewModel?model?=?new?ViewModelProvider(requireActivity()).get(SharedViewModel.class);model.getSelected().observe(getViewLifecycleOwner(),?item?->?{//?Update?the?UI.});} }

請注意,這兩個 Fragment 都會檢索包含它們的 Activity。這樣,當這兩個 Fragment 各自獲取 ViewModelProvider 時,它們會收到相同的 SharedViewModel 實例(其范圍限定為該 Activity)。

此方法具有以下優勢:Activity 不需要執行任何操作,也不需要對此通信有任何了解。除了 SharedViewModel 約定之外,Fragment 不需要相互了解。如果其中一個 Fragment 消失,另一個 Fragment 將繼續照常工作。每個 Fragment 都有自己的生命周期,而不受另一個 Fragment 的生命周期的影響。如果一個 Fragment 替換另一個 Fragment,界面將繼續工作而沒有任何問題。

總結

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

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

往期推薦

對數據“投入”卻沒有“產出”?聽聽Gartner的分析

長跑11年,騰訊開源的變與不變

邊緣應用增長800%,聽聽Akamai邊緣部署的經驗

CSDN云原生Meet up深圳站與你不見不散!

點分享

點收藏

點點贊

點在看

總結

以上是生活随笔為你收集整理的全新的 Fragment 通信方式的全部內容,希望文章能夠幫你解決所遇到的問題。

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