安卓监听器串行化

5

我有一个扩展了DialogFragmentCustomDialog类。我重写了onCreateDialog方法,以获取我想要的自定义对话框。

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    dialog = new Dialog(activity, styleId);
    view = activity.getLayoutInflater().inflate(layoutId, null);
    dialog.setContentView(view);
    if (listener != null) {
        listener.onViewInit(view, this);
    }
    return dialog;

}

这是自定义对话框创建代码。在视图被填充后,我调用类型为OnViewInitListener的接口和Serializable的listener.onViewInit(view, this)监听器方法,将自定义代码绑定到视图(视图文本、监听器等),以便在旋转时不会丢失按钮按下逻辑。

@Override
public void onSaveInstanceState(Bundle bundle) {
    bundle.putInt("layoutId", layoutId);
    bundle.putInt("styleId", styleId);
    bundle.putSerializable("listener", listener);
    super.onSaveInstanceState(bundle);

}

public RsCustomDialog setOnListenerAssignment(OnViewInitListener listener) {
    this.listener = listener;
    return this;
}

当我从Activity实现OnViewInitListener时,在方向更改时一切都按预期工作:每次重建片段时都会调用onCreateDialog,并且没有包错误,但是当我按应用程序历史记录按钮(最右侧)时,出现以下错误:
android buttons
(来源:cbsistatic.com) 我收到了这个错误信息:
10-09 11:09:38.256: E/AndroidRuntime(24153): FATAL EXCEPTION: main
10-09 11:09:38.256: E/AndroidRuntime(24153): java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = base.RsCustomDialog$OnClickListener)
10-09 11:09:38.256: E/AndroidRuntime(24153):    at android.os.Parcel.writeSerializable(Parcel.java:1279)
10-09 11:09:38.256: E/AndroidRuntime(24153):    at android.os.Parcel.writeValue(Parcel.java:1233)
10-09 11:09:38.256: E/AndroidRuntime(24153):    at android.os.Parcel.writeMapInternal(Parcel.java:591)
10-09 11:09:38.256: E/AndroidRuntime(24153):    at android.os.Bundle.writeToParcel(Bundle.java:1627)
10-09 11:09:38.256: E/AndroidRuntime(24153):    at android.os.Parcel.writeBundle(Parcel.java:605)
10-09 11:09:38.256: E/AndroidRuntime(24153):    at android.support.v4.app.FragmentState.writeToParcel(Fragment.java:133)

我猜这是因为当我从我的活动中实现OnViewInitListener时,Java会将活动变量隐式放置在已实现的对象中,而Parcel无法处理活动的封装。

有人可以建议如何解决这个问题,或提供更好的解决方案吗?

4个回答

6
您无法序列化和还原一个监听器。 序列化(包括使用 Parcelable)保存对象实例的状态,反序列化将该状态放入新的对象实例中。
监听器没有状态 - 这就是为什么您的 Parcelable 实现没有保存或还原任何内容。监听器变量是对对象实例的引用(已知实现了监听器接口的对象)。如果创建了该对象的新实例(例如由于旋转或进程因低内存而被杀死),则对话框尝试恢复指向先前实例的指针并没有帮助。先前的实例已不再存在,并且在调用 onSaveInstanceState 时新创建的(正确的)实例不存在。
两种可能的替代方案:
  • 如果监听器旨在成为对话框所附加的 Activity,则可以在 DialogFragment 的 onAttach 方法中还原它
  • 如果不是,则 Activity 可以在创建(或重新创建)时设置 Fragment 监听器。要获取对自动还原的 Fragment 实例的引用,可以使用 getFragmentManager().findFragmentById(id)getFragmentManager().findFragmentByTag(tag)

第二个选项有点棘手,因为如果活动被重新创建,它将失去任何存储的监听器...除非使用静态变量或在应用程序中存储,但这不是良好的实践... - User
@lxx 我同意,静态变量不是一个好主意 - 我也想不出应用实例有意义的情况,但这并不意味着它不存在。我对第二个选项的想法是要么创建一个新的监听器实例,要么将从FragmentManager检索到的片段实例设置为监听器。 - Stan Kurdziel

1
你的 OnViewInitListener 应该是静态的、可序列化的,并包含所有的可序列化字段。如果你从中引用了 Activity,则做错了。要解决这个问题,你可以:
  1. 引用存储在静态 WeakReference 变量中的 activity 实例,在 activity 创建时进行填充。
  2. 使用广播接收器
  3. 当 fragment 以新的和适当的上下文恢复时,重新注册监听器。

嗨,谢谢回复!我可以告诉你我已经检查了你列表中的1和3: 1)我从BaseActivity中的静态活动引用引用变量。 3)我不想重新注册侦听器,我的目标是注册一次,然后忘记它。在配置更改时,它应该自动恢复侦听器。但我总是在CustomDialog类中使用适当的上下文。2- 我不知道你指的是什么,它如何帮助? - Giorgi EgoTwin Khutsishvili

0

你可以在onResume和onStop方法中编写一些内容,然后在onStop方法中取消注册监听器,在onResume方法中注册监听器。


这更像是一条评论而不是答案。 - danglingpointer
good point though wnp - AndreasReiff

-1

好的,我解决了!我所做的是按照以下方式实现了Parcable

public abstract class OnClickListener implements DialogInterface.OnClickListener, Parcelable {
    @Override
    public abstract void onClick(DialogInterface dialog, int which);

    @Override
    public void writeToParcel(Parcel dest, int flags) {

    }

    @Override
    public int describeContents() {
        return 0;
    }
}

所以我的ConfirmDialog代码保持不变:
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    return new AlertDialog.Builder(getActivity())
        .setTitle(title).setMessage(text)
        .setPositiveButton(R.string.yes, onYes)
        .setNegativeButton(R.string.no, onNo).create();
}

@Override
public void onSaveInstanceState(Bundle bundle) {
    super.onSaveInstanceState(bundle);
    bundle.putParcelable("onYes", onYes);
    bundle.putParcelable("onNo", onNo);
}

唯一的限制是不要在onClick方法中使用非Parcelable的自动变量。以下是我展示此对话框的示例:

       showConfirmDialog(getString(R.string.sure_want_to_exit), new base.dialog.OnClickListener() {

        @Override
        public void onClick(DialogInterface dialog, int which) {
            ((NewProtocol) getCurrentActivity()).exit = true;               
            getCurrentActivity().finish();
        }
    }, null);

getCurrentActivity() 是一个静态方法,用于返回当前活动的活动。


9
什么都不写到包裹里什么也得不到。这样根本不会持久化或反序列化任何东西。当实例状态被恢复时,您需要从活动中设置侦听器。不要将指针永久化为无效值。这个答案比崩溃更糟糕,因为它会在内存限制达到边缘情况下悄悄地中断工作中的侦听器。这可能导致生产中出现巨大的错误却未被发现。 - colintheshots
为什么它能够工作呢?实际上,即使没有在onSaveInstanceState中显式持久化,它也可以正常工作。getArguments().getParcelable("listener")始终返回传递给Bundle的相同对象,无论手机方向改变了多少次。 - Saad Farooq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接