【问题标题】:When does Android serialize objects?Android什么时候序列化对象?
【发布时间】:2015-09-05 06:51:52
【问题描述】:

我正在开发一个 Android 项目,我想将自定义类 MainActivityModel 传递给 FragmentMainActivityPlaceholderFragment

我已将MainActivityModel 设为可序列化:

public class MainActivityModel implements Serializable{

    public int current = 0;
    public int pageCount = 0;

    public boolean pristine = true;

    // Stores the fetched dataMap
    public ArrayList<HashMap<String, String>> arrayList;

    public MainActivityModel() {
        this.arrayList = new ArrayList<>();
    }

    public String getCategory() {
        return Util.categories[current];
    }

    public CharSequence getmTitle () {
        return  Util.toTitleCase(
                Util.mapCategoryPretty(Util.categories[current]));
    }
}

我将它传递给Fragment,如下所示:

public static MainActivityPlaceholderFragment newInstance(MainActivityModel mainActivityModel) {
    MainActivityPlaceholderFragment fragment = new MainActivityPlaceholderFragment();
    Bundle args = new Bundle();
    args.putSerializable(ARG_DATA_MODEL, mainActivityModel);
    fragment.setArguments(args);
    Log.v(LOG_TAG, "Created: " + mainActivityModel.getmTitle());
    return fragment;
}

我这样访问它:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mainActivityModel = (MainActivityModel) getArguments().getSerializable(ARG_DATA_MODEL);
    mMainActivityPlaceholderFragmentView = new MainActivityPlaceholderFragmentView(this, mainActivityModel);

    mCallbacks.onPlaceholderFragmentCreated(mainActivityModel.current);
}

我最初认为(在阅读了我在下面提到的答案之后),序列化将数据转换为字节并在需要时随后恢复它们。所以我的对象会被复制。如果我只想访问数据,这没关系。但我也想从片段中修改实际模型(在MainActivity 中引用)。

为了进行实验,我在 Fragment 中将 pristine 设置为 false,并将其记录到 MainActivity 中,确实是错误的。

但如果可序列化是按值传递的,这是怎么回事?

我读过的:

  1. What is object serialization?
  2. R: Pass by reference
  3. what is Serializable in Java
  4. What is serialization in Java?

【问题讨论】:

  • 它是否可序列化并不重要......捆绑无法处理引用的点......它总是序列化/反序列化(parcel/unparcel)......想想这种情况:具有某些 Intent 的活动进入后台,甚至在您返回它时被杀死......猜猜为什么它会恢复意图?它不是引用...系统只需将其保存到包裹中并在需要时重新创建
  • 这是我的问题。如果它正在复制对象,更改如何反映在MainActivity 中?更改应仅限于片段。
  • @prakharsingh95 复制 Object 你理解什么?
  • 它不是同一个对象...对原始对象所做的更改将不适用于通过捆绑包传递的对象(我的意思是“在另一侧”的那个)...
  • @AnkurAnand 它将对象转换为字节,当我再次从Bundle get 它们时,它将序列化表示转换为对象。

标签: java android android-fragments serialization pass-by-reference


【解决方案1】:

您描述的行为看起来很像Parcel active objects,而不是 sun-java 样式的序列化。

【讨论】:

    【解决方案2】:

    Serializable 对象的引用仍然是对象引用,它与传递List 对象或Foo 对象没有区别。令人困惑的部分是 ifwhere 序列化发生。

    来自android.app.Fragment.setArguments(Bundle)的文档:

    此处提供的参数将在片段销毁和创建过程中保留。

    有两种方法可以实现:

    • 使Bundle 仅存储原始字节,并对每个get/put 操作进行序列化/反序列化。
    • 允许Bundle 保存活动对象,并在需要销毁/重新创建片段时要求它序列化/反序列化所有内容。

    显然,第一个选项非常效率低下:get/put 操作比活动/片段生命周期更改频繁得多。因此,Android 只会在需要时在生命周期发生变化时序列化/反序列化。

    这会导致您的用例出现“奇怪”的行为。您假设您的Serializable 对象由Bundle立即序列化,而Bundle 只是保存对您的对象的引用。由于片段在newInstanceonCreate 调用之间没有被破坏,因此您看到完全相同的Bundle 持有完全相同的引用。

    当然,您不应该不依赖这些参考来保持原样。每当您的应用程序被要求保持其状态时(例如,当进入后台、旋转屏幕或系统需要释放 RAM 时),这些对象都会被序列化并且引用消失。对象将从序列化数据中重新创建,但它们将具有不同的引用。

    【讨论】:

    • 对不起,我不得不... 不依赖是这里最重要的...我很确定这是某种优化...并且可能不适用于“原始”片段的某些 API 级别
    • 好吧,Android 可以做到这一点,为什么 Android 不能简单地允许我在包中存储引用。我的对象的生命周期与父活动的生命周期相同。
    • 如此处所述 (stackoverflow.com/a/14917265/3887393),无法将不可编组的对象存储在包中
    • @prakharsingh95 “为什么 android 不能简单地允许我在包中存储引用。我的对象的生命周期与父活动的生命周期相同。” 因为你的父母活动也可以被销毁/重新创建。例如,如果您在打开父 Activity 的情况下旋转屏幕,则可能需要销毁整个 Activity(及其片段)并重新创建以使用不同的 Activity 布局文件。
    • @MattiasBuelens 是的。如果我的父活动被破坏,那么在重新创建时,包括片段在内的所有内容都将被重新创建。我的意思是,如果我可以保证该对象将比片段更老并且总是比它长,那么问题是什么?
    【解决方案3】:

    序列化会创建对象的深层副本,这意味着如果您序列化然后反序列化包含其他对象的对象,您将获得新的独立对象(带有新引用),所有内容的副本,绝对不会引用您仍然拥有的对象在堆上。因此,如果您修改刚刚反序列化的对象,您只会修改这些对象,而不是您在堆上可能拥有的任何先前引用。

    如果您想协调刚刚反序列化的引用与内存中的对象,则必须对其进行编码。

    【讨论】:

    • "如果您想协调刚刚反序列化的引用与内存中的对象,您必须对其进行编码。"是的。我很担心,因为我没有这样做,它仍然有效。我想明白为什么。我还在问题中添加了如何在 cmets 中验证此行为。
    • 这可能是因为 java String 行为。字符串有一个特定的分配系统,可以避免受骗。有一个内部字符串池,如果您创建一个新的字符串对象,如果您的新字符串已经存在,它将查看该池。如果存在,您将获得对现有字符串的引用。由于字符串是不可变的,这不是问题。
    【解决方案4】:

    在 Java 中,所有对象都通过引用传递,只有原始类型(int、float、long...)通过值传递。

    我真的不知道 Serializable 是如何工作的,但如果它转换为不是原始类型的 byte[] ,那么这就是它工作的原因。

    如果您将 Serializable 类写入文件并将其以 ASCII 格式打开,您会看到一种递归的 toString(),后面可能是字节数据。

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2012-06-13
      • 1970-01-01
      • 2013-01-10
      • 1970-01-01
      • 2012-01-21
      • 2010-10-01
      • 1970-01-01
      • 2015-05-10
      相关资源
      最近更新 更多