Android 什么时候序列化对象?
When does Android serialize objects?
我正在做一个 Android 项目,我想将自定义 class MainActivityModel
传递给 Fragment
、MainActivityPlaceholderFragment
。
我已将 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是按值传递,这是怎么回事?
我读过的内容:
- What is object serialization?
- R: Pass by reference
- what is Serializable in Java
- What is serialization in Java?
在Java中,所有对象都是通过引用传递的,只有基本类型(int、float、long...)是通过值传递的。
我真的不知道 Serializable 是如何工作的,但如果它转换为不是原始类型的 byte[],那么这就是它工作的原因。
如果您将可序列化 class 写入文件并将其作为 ASCII 打开,您会看到一种递归的 toString() 后跟可能是字节数据的内容。
希望对您有所帮助。
序列化会创建对象的深层副本,这意味着如果您序列化然后反序列化包含其他对象的对象,您将获得新的独立对象(具有新引用),所有内容的副本,绝对不会引用您的对象仍然在堆上。因此,如果您修改刚刚反序列化的对象,您将只会修改这些对象,而不会修改您在堆上可能拥有的任何先前引用。
如果您想协调刚刚反序列化的引用与内存中的对象,则必须对其进行编码。
对 Serializable
对象的引用仍然是对象引用,它与传递 List
对象或 Foo
对象没有区别。令人困惑的部分是 if 和 where 序列化发生。
来自 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
只是持有对您的对象的引用。由于片段在 newInstance
和 onCreate
调用之间没有被破坏,您看到完全相同的 Bundle
持有完全相同的引用。
当然,您不应该不依赖这些引用来保持完整。任何时候你的应用程序被要求保持它的状态(例如,当进入后台时,当旋转屏幕时,或者当系统需要释放 RAM 时),这些对象被序列化并且引用消失了。将从序列化数据重新创建对象,但它们将具有不同的引用。
您所描述的行为看起来很像 Parcel active objects,而不是 sun-java 风格的序列化。
我正在做一个 Android 项目,我想将自定义 class MainActivityModel
传递给 Fragment
、MainActivityPlaceholderFragment
。
我已将 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是按值传递,这是怎么回事?
我读过的内容:
- What is object serialization?
- R: Pass by reference
- what is Serializable in Java
- What is serialization in Java?
在Java中,所有对象都是通过引用传递的,只有基本类型(int、float、long...)是通过值传递的。
我真的不知道 Serializable 是如何工作的,但如果它转换为不是原始类型的 byte[],那么这就是它工作的原因。
如果您将可序列化 class 写入文件并将其作为 ASCII 打开,您会看到一种递归的 toString() 后跟可能是字节数据的内容。
希望对您有所帮助。
序列化会创建对象的深层副本,这意味着如果您序列化然后反序列化包含其他对象的对象,您将获得新的独立对象(具有新引用),所有内容的副本,绝对不会引用您的对象仍然在堆上。因此,如果您修改刚刚反序列化的对象,您将只会修改这些对象,而不会修改您在堆上可能拥有的任何先前引用。
如果您想协调刚刚反序列化的引用与内存中的对象,则必须对其进行编码。
对 Serializable
对象的引用仍然是对象引用,它与传递 List
对象或 Foo
对象没有区别。令人困惑的部分是 if 和 where 序列化发生。
来自 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
只是持有对您的对象的引用。由于片段在 newInstance
和 onCreate
调用之间没有被破坏,您看到完全相同的 Bundle
持有完全相同的引用。
当然,您不应该不依赖这些引用来保持完整。任何时候你的应用程序被要求保持它的状态(例如,当进入后台时,当旋转屏幕时,或者当系统需要释放 RAM 时),这些对象被序列化并且引用消失了。将从序列化数据重新创建对象,但它们将具有不同的引用。
您所描述的行为看起来很像 Parcel active objects,而不是 sun-java 风格的序列化。