【问题标题】:Can I pass an interface to bundle of a Fragment?我可以将接口传递给 Fragment 的捆绑包吗?
【发布时间】:2018-10-19 00:58:35
【问题描述】:

由于 Fragment 需要在配置更改后继续存在,Android 强烈建议我们在创建 Fragment 时实现自己的newInstance() 方法,将必要的数据传递给它的Bundle,而不是使用new MyFragment()

现在,问题是,我想向这个 Fragment 传递一个回调(接口)

起初我尝试将我的界面设为extends Serializable,并简单地使用args.putSerializable("myInterface", myInterface)
但是当 Android 尝试保存它的状态(包裹它)时,会抛出一个异常(Parcelable 遇到 IOException 写入可序列化对象)。

我知道官方的做法是让调用者Activity实现回调,并在片段的onAttach()期间将回调引用到Activity。
是的,它有效,但感觉很奇怪,因为我实际上是在创建一个库,现在我没有要求用户将回调传递给构建器,而是必须告诉他们你的调用者活动必须实现某个接口。
更重要的是,如果我想从另一个Fragment 显示这个Fragment 怎么办? onAttach() 始终附加到 Activity

那么,有没有一种方法可以让用户将回调传递给片段,并且能够在配置更改(例如设备旋转)中幸存下来?

更新

根据要求,这里是我要传递的接口:

public interface OnImageSelectedListener extends Serializable {
    void onImageSelected (String uri);
}

以及我是如何构建这个接口的:

new OnImageSelectedListener() {
        @Override
        public void onSingleImageSelected(String uri) {
            Glide.with(MainActivity.this).load(uri).into(ivImage);
        }
}

【问题讨论】:

  • 您的接口是否使用任何不实现可序列化的对象?
  • 它只是一个方法的接口。啊,但是该方法将Uri 作为参数传递。 (void onSomethingDone(Uri uri);。这有关系吗?(顺便说一句,Uri 实现了Parcelable

标签: java android android-fragments serialization interface


【解决方案1】:

您遇到此问题的原因是, Glide 是不可序列化的数据,您无法对其进行序列化。由于序列化是由 Java 定义并在 JVM 上完成的过程,它不理解Parcelable,因此失败并出现错误。即使您只是在回调方法中使用它们,这也适用。

但由于您正在开发库,您将无法控制回调接口的使用。所以解决方案是通过序列化来避免发送这个对象。

相反,当您的库的用例启​​动时(必须有一些入口点来触发用例),您需要请求您将引用直接传递给您的方法。然后,您可以维护引用并在必要时执行回调。您需要实现一些包装器来实现它。并通过LocalBroadcastReceiver进行内部交流

如果上述方法不可行,那么唯一的解决方案是要求您的图书馆用户注册 LocalBroadcastReceive 并通过广播传递结果。

【讨论】:

  • 我认为你错了。首先,我尝试了您建议的将Uri 替换为String 的方法,但抛出了相同的异常。其次,如果我尝试扩展Parcelable instead,当我声明一个匿名回调时,我还必须实现 Parcelable 的必要方法。我的意思是,我认为这与接口方法中传递的参数无关。就像您不必关心实现了Serializable 的POJO 的方法参数是否也实现了Serializable
  • 仅当您尝试在回调中使用不可序列化的对象时才会发生此异常。你能用通过回调发送的代码更新你的问题吗?
  • 即使你创建了匿名类并且“只传递参数”编译器仍然会创建必要的访问器方法,其数据需要被序列化。
  • 您可以查看更新的答案
  • 后来发现这其实是不可行的。我想要做的是,在操作系统重新创建对话框片段时恢复所有内容。但即使我可以保存回调,活动、视图也会被销毁并重新创建,从而产生不同的实例。旧的回调没有用。我现在正试图在onSaveInstanceState 之前关闭 DialogFragment,但还没有找到方法......
【解决方案2】:

最后,我得出结论:

我们不应该保留回调。

想象一下回调执行以下代码:
Glide.with(context).load(image).into(imageView);

当设备旋转时,上面属于一个activity或另一个fragment的imageView也会被销毁并重新创建。 保留回调中引用的旧ImageView 不再存在。

这要么让 Glide 抛出异常;要么或泄露旧的ImageView,从而泄露整个Activity

这就是为什么我们应该始终将回调引用到附加的Activity
而如果调用者是Fragment,而不是引用onAttach()中的回调,只需引用onCreateView()中的父片段:

if (getParentFragment() != null && getParentFragment() instanceof YourInterface) {
    yourCallback = (YourInterface) getParentFragment();
}

所以如果要回答原来的问题,

我可以将接口传递给 Fragment 的捆绑包吗?

答案是,可能不,但你不应该。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-09
    • 1970-01-01
    • 2018-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多