据我了解,Bundle
属于Parcelable
Android执行序列化的方式。它用于例如在活动之间传递数据。Parcelable
但是我想知道,例如,在将业务对象的状态保存到内部存储器的情况下,使用而不是经典序列化是否有任何好处?它会比经典方式更简单或更快吗?我应该在哪里使用经典序列化以及在哪里更好地使用捆绑包?
9 回答
来自“Pro Android 2”
注意:看到 Parcelable 可能会引发问题,为什么 Android 不使用内置的 Java 序列化机制?事实证明,Android 团队得出的结论是,Java 中的序列化速度太慢,无法满足 Android 的进程间通信要求。因此,该团队构建了 Parcelable 解决方案。Parcelable 方法要求您显式序列化类的成员,但最终,您可以更快地序列化对象。
还要意识到 Android 提供了两种机制,允许您将数据传递给另一个进程。第一种是使用意图将捆绑包传递给活动,第二种是将 Parcelable 传递给服务。这两种机制不可互换,不应混淆。也就是说,Parcelable 并不意味着传递给活动。如果您想启动一个活动并传递一些数据,请使用捆绑包。Parcelable 仅用作 AIDL 定义的一部分。
Serializable
在 Android 上速度慢得可笑。事实上,边界线在很多情况下是无用的。
Parcel
并且Parcelable
速度非常快,但它的文档说你不能将它用于通用序列化到存储,因为实现会随 Android 的不同版本而变化(即操作系统更新可能会破坏依赖它的应用程序)。
以合理的速度将数据序列化到存储的问题的最佳解决方案是自己滚动。我个人使用我自己的一个实用程序类,它具有类似的接口Parcel
并且可以非常有效地序列化所有标准类型(以类型安全为代价)。这是它的精简版:
public interface Packageable {
public void readFromPackage(PackageInputStream in) throws IOException ;
public void writeToPackage(PackageOutputStream out) throws IOException ;
}
public final class PackageInputStream {
private DataInputStream input;
public PackageInputStream(InputStream in) {
input = new DataInputStream(new BufferedInputStream(in));
}
public void close() throws IOException {
if (input != null) {
input.close();
input = null;
}
}
// Primitives
public final int readInt() throws IOException {
return input.readInt();
}
public final long readLong() throws IOException {
return input.readLong();
}
public final long[] readLongArray() throws IOException {
int c = input.readInt();
if (c == -1) {
return null;
}
long[] a = new long[c];
for (int i=0 ; i<c ; i++) {
a[i] = input.readLong();
}
return a;
}
...
public final String readString() throws IOException {
return input.readUTF();
}
public final <T extends Packageable> ArrayList<T> readPackageableList(Class<T> clazz) throws IOException {
int N = readInt();
if (N == -1) {
return null;
}
ArrayList<T> list = new ArrayList<T>();
while (N>0) {
try {
T item = (T) clazz.newInstance();
item.readFromPackage(this);
list.add(item);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
N--;
}
return list;
}
}
public final class PackageOutputStream {
private DataOutputStream output;
public PackageOutputStream(OutputStream out) {
output = new DataOutputStream(new BufferedOutputStream(out));
}
public void close() throws IOException {
if (output != null) {
output.close();
output = null;
}
}
// Primitives
public final void writeInt(int val) throws IOException {
output.writeInt(val);
}
public final void writeLong(long val) throws IOException {
output.writeLong(val);
}
public final void writeLongArray(long[] val) throws IOException {
if (val == null) {
writeInt(-1);
return;
}
writeInt(val.length);
for (int i=0 ; i<val.length ; i++) {
output.writeLong(val[i]);
}
}
public final void writeFloat(float val) throws IOException {
output.writeFloat(val);
}
public final void writeDouble(double val) throws IOException {
output.writeDouble(val);
}
public final void writeString(String val) throws IOException {
if (val == null) {
output.writeUTF("");
return;
}
output.writeUTF(val);
}
public final <T extends Packageable> void writePackageableList(ArrayList<T> val) throws IOException {
if (val == null) {
writeInt(-1);
return;
}
int N = val.size();
int i=0;
writeInt(N);
while (i < N) {
Packageable item = val.get(i);
item.writeToPackage(this);
i++;
}
}
}
如果您需要序列化用于存储目的,但又想避免Serializable接口引起的反射速度损失,您应该使用Externalizable接口显式创建您自己的序列化协议。
如果实施得当,这与 Parcelable 的速度相匹配,并且还考虑了不同版本的 Android 和/或 Java 平台之间的兼容性。
这篇文章也可能会澄清一些事情:
在旁注中,它也是许多基准测试中最快的序列化技术,击败了 Kryo、Avro、Protocol Buffers 和 Jackson (json):
http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking
现在看来,这种差异并不那么明显,至少当您在自己的活动之间运行它时不会如此。
根据本网站上显示的测试,Parcelable 在最新设备(如 nexus 10)上快了约 10 倍,在旧设备(如欲望 Z)上快了约 17 倍
所以由你决定是否值得。
也许对于相对较小和简单的类,Serializable 很好,其余的,你应该使用 Parcelable
Parcelable 主要与使用Binder基础设施的 IPC 相关,其中数据作为Parcels传递。
由于 Android 对于大多数(如果不是全部)IPC 任务都非常依赖 Binder,因此在大多数地方实现 Parcelable 是有意义的,尤其是在框架中,因为它允许在需要时将对象传递给另一个进程。它使对象“可移动”。
但是如果你有一个非 Android 特定的业务层,它广泛使用可序列化来保存对象状态,并且只需要将它们存储到文件系统,那么我认为可序列化就可以了。它允许避免 Parcelable 样板代码。
基于这篇文章http://www.mooproductions.org/node/6?page=5 Parcelable 应该更快。
文章中没有提到的是,我不认为可序列化对象将在 AIDL 中用于远程服务。
我只是使用 GSON -> 序列化为 JSON 字符串 -> 从 JSON 字符串恢复对象。
Parcelable 还提供自定义实现,用户有机会通过覆盖 writeToParcel() 来打包他的每个对象,但是序列化并没有这种自定义实现,因为它传递数据的方式涉及 JAVA 反射 API。