是否最好使用 Activity.onAttachFragment 或 Fragment.onAttach 在 Activity 和嵌套片段之间进行通信?

Posted

技术标签:

【中文标题】是否最好使用 Activity.onAttachFragment 或 Fragment.onAttach 在 Activity 和嵌套片段之间进行通信?【英文标题】:Is it preferable to use Activity.onAttachFragment or Fragment.onAttach to communicate between an Activity and a nested fragment? 【发布时间】:2012-10-05 17:21:12 【问题描述】:

android 文档建议,要从 Activity 与托管 Fragment 进行通信,该 Fragment 可以定义一个回调接口并要求宿主 Activity 实现它。基本模式涉及在您的片段中实现onAttach,并将活动转换为回调接口。见http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity。

这是一个为片段提供一些初始化数据以及监听导航回调的示例。

public class HostActivity extends Activity implements FragmentHost 
  @Override
  UiModel getUiModel() 
    return mUiModel;
  
  @Override
  FragmentNavListener getNavListener() 
    return mNavListener;
  
...


public class HostedFragment extends Fragment 
  @Override
  public void onAttach(Activity activity) 
    super.onAttach(activity);
    if (activity instanceof FragmentHost) 
      FragmentHost host = (FragmentHost) activity;
      setUiModel(host.getUiModel());
      setNavListener(host.getFragmentNavListener());
    
  
  ...

将此与在宿主活动中使用onAttachFragment 显式初始化片段进行比较:

public class HostActivity extends Activity 
  @Override
  public void onAttachFragment(Fragment fragment) 
    super.onAttachFragment(fragment);
    if (fragment instanceof HostedFragment) 
      HostedFragment hostedFragment = ((HostFragment) fragment);
      hostedFragment.setUiModel(mUiModel);
      hostedFragment.setNavListener(mNavListener);
    
  
  ...

在我看来,第一种模式似乎有一些缺点:

    它使片段更难在不同的活动中使用,因为 因为所有这些活动都必须实现所需的接口。我可以想象给定片段实例不需要完全由宿主 Activity 配置,但所有潜在宿主 Activity 都需要实现宿主接口的情况。 对于不熟悉所用模式的人来说,这会使代码更难理解。在 onFragmentAttached 中初始化片段似乎更容易理解,因为初始化代码位于创建片段的同一类中。 使用像 Robolectric 这样的库进行单元测试变得更加困难,因为在调用 onAttach 时,您现在必须实现 FragmentHost 而不仅仅是调用 onAttach(new Activity()。

对于那些从事过分散交流活动的人,您认为哪种模式更可取,为什么?在宿主活动中使用onAttachFragment 有什么缺点吗?

【问题讨论】:

【参考方案1】:

我不能亲自谈论测试,但片段/活动回调接口通信有替代方案。

例如,您可以使用事件总线来解耦片段和您的活动。可以在此处找到出色的事件总线:

Otto - An event Bus by Square

Square 的一些非常有才华的工程师正在积极开发它。 您还可以使用打包在 Android 支持库中的 LocalBroadcastManager。

LocalBroadcastManager

来自 Square 的 Eric Burke 有一个演示文稿,他提到了这两个可以在这里找到:

Android App Anatomy

【讨论】:

Otto 现已弃用 LocalBroadcastManager 已弃用 developer.android.com/reference/androidx/localbroadcastmanager/… 并推荐此 developer.android.com/training/basics/fragments/communicating【参考方案2】:

更新:根据最新指南,我们应该使用 sharedViewModel 类在片段和活动之间进行通信。

但是,如果您不使用 viewModel 并使用回调,您仍然应该考虑移除 onAttachFragment 回调,因为它已被弃用

建议的方法是使用 addFragmentOnAttachListener 向与该事务关联的 fragmentManager 添加一个侦听器,而不是 onAttachFragment。

请看下面的例子:

   childFragmentManager.addFragmentOnAttachListener  _, fragment ->
        if (fragment is RetryDialogFragment) 
           //Do your work here.
        
    

您还应该注意添加此侦听器的位置。您很可能希望在执行片段事务之前添加此侦听器,并且应确保您不会多次添加该侦听器。

【讨论】:

在导航组件的情况下应该在哪里使用这个监听器?我用导航组件设置了 BottomNavigationView,并在 BaseActivity 的 onCreate() 中实现了这个监听器。第一次调用片段 onAttach() 时,此侦听器会通知,但下次我手动调用 onAttach() 时,侦听器不会通知。你能帮助我吗?提前致谢 手动调用 onAttach() 是什么意思?我对导航组件不太熟悉,但为了获得通知,您必须使用添加了“addFragmentOnAttachListener”的相同“fragmentManager”进行片段事务。 我有一个场景,点击片段内的按钮后需要重新加载片段。这发生正确并导致再次调用 onAttach() 方法,但侦听器不会再次收到回调。 尝试在 *** 上将其作为一个单独的问题提出,其中包含您如何添加和重新加载片段的代码。【参考方案3】:

我在上一个项目中使用了Fragment.onAttach(...) 模式。我看到了两个优点:

    您可以尽早检查托管活动是否实现了所需的接口,如果没有则抛出异常 在分离片段后保留宿主上下文引用的风险较小

为了利用 2.,您不能像在代码示例中那样存储对 UiModelNavListener 的引用。相反,每当您想与这些实例交互时,您应该使用类似((FragmentHost) getActivity).getNavListener().onNav(this) 的代码,或者((FragmentHost) getActivity).onNav(this)。如果您想避免持续转换,您可以将片段主机存储在您在 onDetach(...) 中设置为 null 的字段中作为中间方法。

我同意从创建片段的 Activity 中初始化片段看起来更直观。

说了这么多,我将在我当前的项目中完全跳过片段。下面的帖子很好地反映了我上一篇的经验教训:https://corner.squareup.com/2014/10/advocating-against-android-fragments.html

【讨论】:

以上是关于是否最好使用 Activity.onAttachFragment 或 Fragment.onAttach 在 Activity 和嵌套片段之间进行通信?的主要内容,如果未能解决你的问题,请参考以下文章

[Java]判断Integer值相等最好不用==最好使用equals

是否可以在不使用 ObjectDataSource 的情况下自定义 GridView(在 ASP.NET 中,最好是 3.5)分页?

html 您是否经常将Flash文件嵌入到html页面中?如果是,您最好保存下面的有效Flash嵌入代码以备将来使用。

使用 Redux,错误消息在哪里产生最好?

是否最好使用HTTP标头重定向到不同的页面/文档,或者合并动态消息以通知用户拒绝访问?

“结巴”中文分词:做最好的 Python 中文分词组件