全新的 Fragment 通信方式

2115932d0e51d591e6fb8e7bff674915.png

作者 | 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() 之前被调用)。

80d201314481762cc74dc3200441d34f.png
  • 当生命周期处于 LifecycleOwner STARTED 的状态之前,如果有多个数据传递,只会接收到最新的值:

7182fd3f0920094cdafa09089d077270.png
  • 当生命周期处于 LifecycleOwner DESTROYED 时,它将自动移除 listener,如果想手动移除 listener,需要调用 FragmentManager.setFragmentResultListener() 方法,传递空的 FragmentResultListener

41a37301baa87f6531d36d4579e00cff.png

从时序图很明显可以总体分析出流程来,大致是在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 之间通信最好的方式是什么?

73b9d64fece9f59f79db694ed97b38d7.png

e4c3c3ff39fd4fa90a48dbcede71bda6.png

往期推荐

对数据“投入”却没有“产出”?听听Gartner的分析

长跑11年,腾讯开源的变与不变

边缘应用增长800%,听听Akamai边缘部署的经验

CSDN云原生Meet up深圳站与你不见不散!

559cf73625697eb32c2442e4735784a5.png

点分享

33eeb444e049c764dd483384f0d42b12.gif

点收藏

b0046ed6abfaf52113ec628abd809bae.png

点点赞

af535f395d6392e4152f8776af6edbb6.gif

点在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/513269.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据传输完整性_电缆监测数据传输系统分析与设计

电缆线路是重要的输电方式&#xff0c;对电缆线路进行监测是保证电缆线路正常工作的重要的条件&#xff0c;研究人员利用嵌入式系统设计了电缆监测数据传输系统。该系统以CAN通信和嵌入式以太网络技术为核心&#xff0c;实现了对电缆及其沟道的实时监测、状态显示及预报警功能&…

大型企业多账号管理“安全心法”

简介&#xff1a; 云上多账号环境下的网络统一管理&#xff0c;是大型分支型企业网络安全防护的必经之路。无论是外企入华、国内企业出海&#xff0c;还是本土集团型企业规模化成长&#xff0c;云上统一网络安全管控与整体安全态势感知&#xff0c;都可以拉齐企业账号间安全水位…

苹果将于 2025 年推出的 Apple Car 长什么样?

整理 | 孙胜出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;据国外媒体报道&#xff0c;苹果公司预计将于2025年推出一款全新的自动驾驶汽车&#xff0c;旨在实现真正意义上的无人驾驶。报道称&#xff0c;基于自动驾驶的理念&#xff0c;苹果理想的汽车没有方向盘…

阿里云中间件首席架构师李小平:云原生实践助力企业高效创新

简介&#xff1a; 通过云原生技术&#xff0c;真正为企业带来更多的业务价值&#xff0c;助力企业整体的业务创新。 作者&#xff1a;李小平 前天我参加了信通院的云原生产业大会&#xff0c;在会场上非常感慨&#xff0c;参加会议的企业非常多&#xff0c;并且来自于各行各业…

cv曲线面积的意义_几何直觉的魅力:sinx曲线下的面积原理是如此的美妙

用“曲线下的面积”来描述积分&#xff0c;就像用一串单词来描述一本书。正弦函数的积分是其曲线下的面积。几何直觉就是:“正弦的积分是沿圆周路径的水平距离。”这句话第一次听说感觉比较抽象&#xff0c;当你理解了就会觉得它非常的美妙一般的思维模式求正弦函数的积分就是&…

OpenInfra 十一年:OpenStack 部署规模超 2500 万计算核心

后疫情时代下&#xff0c;产生海量在线需求&#xff0c;越来越多金融、政府、教育、通信和医疗保健等上云业务需依赖现代云基础设施来正常运行。其中开源提供了一种更具成本效益的开发方式&#xff0c;据最新《2021 年度 Octoverse 报告》显示&#xff0c;2021 年 GitHub 开发者…

集群镜像:实现高效的分布式应用交付

简介&#xff1a; Docker 解决了单个容器的镜像化问题&#xff0c;而 sealer 通过把整个集群打包&#xff0c;实现了分布式软件的 Build Share Run。 作者 | fanux.中弈 什么是集群镜像 顾名思义&#xff0c;和操作系统 .iso 镜像或 Docker 镜像类似&#xff0c;集群镜像是用一…

比开源快30倍的自研SQL Parser设计与实践

简介&#xff1a; SQL作为一种领域语言&#xff0c;最早用于关系型数据库&#xff0c;方便管理结构化数据&#xff1b;SQL由多种不同的类型的语言组成&#xff0c;包括数据定义语言&#xff0c;数据控制语言、数据操作语言&#xff1b;各数据库产品都有不同的声明和实现&#x…

SLS控制台内嵌操作指南

简介&#xff1a; SLS控制台内嵌操作指南 一、机制 详见&#xff1a;https://help.aliyun.com/document_detail/74971.html 二、操作 2.1 子账号操作&#xff08;主账号身份操作&#xff09; 登陆ram控制台&#xff0c;创建子账号。给子账号授予AliyunSTSAssumeRoleAccess权…

装linux服务器进去配置界面,在CentOS 8 Linux上安装和配置SuiteCRM的步骤

本文介绍在CentOS 8 Linux服务器上安装和配置SuiteCRM的详细步骤&#xff1a;更新系统、安装PHP、安装MariaDB和Nginx Web服务器、安装SuiteCRM、配置SuiteCRM Web访问界面。SuiteCRM是由SalesAgility团队开发和维护的开源企业级CRM应用程序&#xff0c;该产品最初是SugarCRM社…

Dev Lake 0.4.0 版本:开源、开放的研发效能数据平台

建设研发工具链后&#xff0c;效能提升如何更进一步&#xff1f; 工程师们反馈流程体验确实有所提升&#xff0c;和业务同事的沟通似乎也愉快了一些——但研发团队依然需要量化数据作为抓手&#xff0c;一方面佐证先前实践优化的有效性&#xff0c;另一方面为持续的效能提升寻找…

tensorflow图形识别_手把手教你使用TF服务将TensorFlow模型部署到生产环境

摘要&#xff1a; 训练好的模型不知道如何布置到生产环境&#xff1f;快来学习一下吧&#xff01;介绍将机器学习(ML)模型应用于生产环境已成为一个火热的的话题,许多框架提供了旨在解决此问题的不同解决方案。为解决这一问题&#xff0c;谷歌发布了TensorFlow(TF)服务&#xf…

一文读懂 - 云上用户如何灵活应用定制化网络服务

简介&#xff1a; 在将传统数据中心业务迁移上云的过程中&#xff0c;如何将云下基于不同业务场景和设备角色灵活变化的网络配置基于云上网络统一服务能力进行转换&#xff0c;用户及其业务架构通常会面临诸多的挑战。阿里云混合云网络技术团队和阿里云网络产品团队自主创新研发…

linux bash环境,Win10系统怎样启用Linux Bash环境

不久前&#xff0c;微软召开了Build 2016大会&#xff0c;会上微软宣布将在Windows10系统中内置Linux Bash&#xff0c;这一消息引起了非常大的轰动。到了Windows10内部预览版Build 14316&#xff0c;该特性终于面世了。系统城小编将在本文为大家详细介绍下Windows10启用Linux …

云上安全保护伞--SLS威胁情报集成实战

简介&#xff1a; 威胁情报是某种基于证据的知识&#xff0c;包括上下文、机制、标示、含义和能够执行的建议。 什么是威胁情报 根据Gartner对威胁情报的定义&#xff0c;威胁情报是某种基于证据的知识&#xff0c;包括上下文、机制、标示、含义和能够执行的建议。威胁情报描…

数据备份资深老牌厂商 Commvault 的新玩法

作者 | 宋慧 出品 | CSDN 云计算 头图 | 付费下载于视觉中国 已经连续十年&#xff0c;被权威分析机构 Gartner 企业备份与恢复软件魔力象限评为领导者&#xff08;leaders&#xff09;的 Commvault&#xff0c;在数据备份和恢复领域的技术实力和优势毋庸置疑。不过&#xf…

docker 远程连接 文件看不到_pycharm连接远程linux服务器的docker

在我们利用远程服务器部署的docker调试深度学习模型时&#xff0c;需要将代码传来传去&#xff0c;很不方便。这里我们介绍pycharm连接远程服务器docker的方法。首先我们启动一个新的pytorch容器&#xff0c;命令如下。关于此步骤更详细的说明&#xff0c;参见ubuntudocker使用…

「技术人生」第4篇:技术、业务、组织的一般规律及应对策略

简介&#xff1a; 本文讨论了如何让技术一号位能够从理论上、以宏观的视角看清日常工作息息相关的事物的发展规律&#xff0c;从而为顺应规律办事或者创造条件打破规律提供理论依据。 往期技术一号位方法论系列文章&#xff1a; 「技术人生」第1篇&#xff1a;什么是技术一号位…

python贴吧顶贴_Python实现百度贴吧自动顶贴机

开发这款小工具&#xff0c;我们需要做一些准备&#xff1a; url.txt&#xff1a;多个需要顶起的帖子地址。 reply&#xff1a;多条随机回复的内容。 selenium&#xff1a;浏览器自动化测试框架 首先&#xff0c;我们先使用pip完成selenium的安装。 示例代码&#xff1a; pip i…

腾讯云成为国内首家FinOps基金会顶级会员

11月24日&#xff0c;腾讯云正式宣布加入FinOps基金会&#xff0c;作为国内首家FinOps基金会顶级会员&#xff0c;腾讯云将联合FinOps基金会&#xff0c;全面推进对FinOps标准和最佳实践的贡献&#xff0c;为企业提供云财务管理的最佳解决方案。 “作为中国云原生技术和应用的领…