27

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);
    }
  }
  ...
}

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

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

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

4

3 回答 3

6

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

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

Otto - Square 的活动巴士

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

本地广播管理器

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

Android 应用剖析

于 2012-10-15T23:08:08.437 回答
3

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

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

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

请参见下面的示例:

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

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

于 2021-07-09T04:12:45.467 回答
2

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

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

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

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

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

于 2015-06-20T21:04:31.117 回答