Android 什么时候序列化对象?

When does Android serialize objects?

我正在做一个 Android 项目,我想将自定义 class 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 中,这确实是错误的。

但是如果serializable是按值传递,这是怎么回事?

我读过的内容:

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

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

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

如果您将可序列化 class 写入文件并将其作为 ASCII 打开,您会看到一种递归的 toString() 后跟可能是字节数据的内容。

希望对您有所帮助。

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

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

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

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

The arguments supplied here will be retained across fragment destroy and creation.

有两种方法可以实现:

  • 使 Bundle 仅存储原始字节,并且 serialize/deserialize 用于每个 get/put 操作。
  • 允许 Bundle 保存活动对象,并在片段需要 destroyed/recreated.
  • 时要求它 serialize/deserialize 一切

显然,第一个选项非常效率低下:get/put操作比activity/fragment生命周期变化频繁得多。因此,Android 只会 serialize/deserialize 需要时 生命周期变化。

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

当然,您不应该不依赖这些引用来保持完整。任何时候你的应用程序被要求保持它的状态(例如,当进入后台时,当旋转屏幕时,或者当系统需要释放 RAM 时),这些对象被序列化并且引用消失了。将从序列化数据重新创建对象,但它们将具有不同的引用。

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