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

时间:2012-10-15 17:12:02

标签: android design-patterns android-fragments

Android文档建议,要从活动到托管片段进行通信,片段可以定义回调接口,并要求主机活动实现它。基本模式涉及在片段中实现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);
    }
  }
  ...
}

对我来说,似乎第一种模式有一些缺点:

  1. 从而使得碎片更难以从不同的活动中使用 因为所有这些活动都必须实现所需的接口。我可以想象一下给定的片段实例不需要由主机活动完全配置的情况,但所有潜在的主机活动都需要实现主机接口。
  2. 对于不熟悉所使用模式的人来说,这会使代码难以理解。在onFragmentAttached中初始化片段似乎更容易理解,因为初始化代码存在于创建片段的同一个类中。
  3. 使用像Robolectric这样的库进行单元测试变得更加困难,因为在调用onAttach时,你现在必须实现FragmentHost,而不是只调用onAttach(new Activity()。
  4. 对于那些已经完成碎片通信活动的人,您认为哪种模式更可取,为什么?从主机活动中使用onAttachFragment是否存在缺陷?

3 个答案:

答案 0 :(得分:6)

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

例如,您可以使用事件总线来分离片段和活动。这里有一个很棒的活动巴士:

Otto - An event Bus by Square

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

LocalBroadcastManager

来自广场的Eric Burke有一个演讲,他提到了两个可以在这里找到的地方:

Android App Anatomy

答案 1 :(得分:2)

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

  1. 您可以提前检查托管活动是否实现了所需的接口,如果不是
  2. 则抛出异常
  3. 片段分离后保留托管上下文引用的风险较小
  4. 为了利用2.,您不能像在代码示例中那样存储对UiModelNavListener的引用。相反,只要您想要与这些实例进行交互,就应该使用((FragmentHost) getActivity).getNavListener().onNav(this)((FragmentHost) getActivity).onNav(this)之类的代码。如果要避免常量转换,可以将片段主机存储在onDetach(...)中设置为空的字段中作为中间接地方法。

    我同意从创建它的活动初始化片段似乎更直观。

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

答案 2 :(得分:1)

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

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

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

请看下面的例子:

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

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