使用Parcelable而不是序列化对象的好处


97

据我了解,BundleParcelable属于Android执行序列化的方式。例如,它用于在活动之间传递数据。但是我想知道,例如,如果将Parcelable业务对象的状态保存到内部存储器中,使用经典序列化代替传统序列化是否有任何好处?它会比经典方式更简单或更快速吗?我应该在哪里使用经典序列化,哪里可以更好地使用包?

Answers:


99

来自“ Pro Android 2”

注意:看到Parcelable可能已经触发了一个问题,为什么Android不使用内置的Java序列化机制?事实证明,Android团队得出的结论是,Java的序列化速度太慢,无法满足Android的进程间通信要求。因此,团队构建了Parcelable解决方案。Parcelable方法要求您显式地序列化类的成员,但是最后,您可以更快地序列化对象。

还应认识到Android提供了两种机制,可让您将数据传递到另一个进程。第一个是使用意图将捆绑包传递给活动,第二个是将Parcelable传递给服务。这两种机制是不可互换的,不应混淆。也就是说,Parcelable并非要传递给活动。如果要启动活动并将其传递一些数据,请使用捆绑包。可打包仅用于AIDL定义的一部分。


9
什么是“ Android Pro 2”?
AlikElzin-kilaka'4

79
第二段是不正确的,您可以使用包将Parcelable作为参数传递给活动...
Ixx 2012年

3
当我序列化对象时,我创建了一个getBundle方法,然后从writeToParcelas中调用该方法,dest.writeBundle(getBundle());并且对象中的两个选项都自动可用。这里提到了一些有趣的用于活动对象的Parcel功能:developer.android.com/reference/android/os/Parcel.html
mikebabcock 2012年

2
@lxx:我想知道为什么需要通过包裹将可包裹物品传递给活动。IMO如果这样做,则不必要地增加了一层级别的序列化,而没有别的。
2012年

4
Philippe Breault在这方面写了一篇不错的文章,还添加了性能测试。developerphil.com/parcelable-vs-serializable
WonderCsabo

23

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++;
        }
    }

}

2
使用您的自定义类与仅实现Externalizable接口并执行相同操作之间有什么区别?
Carrotman42年

1
Bundle也会序列化字段名称……它不适合成千上万个对象。
Reuben Scratton 2012年

1
抱歉,发明了另一个序列化程序很烂 -现在只有另一个“可打包”要处理。库有很多可供选择的内容(不同之处在于该库经过审查,测试并使用其他人使用的格式):ProtocolBuffers,JSON,XML等。在这方面,Android库确实很烂。

2
我不认为开头的句子已经成立了(五年后)。我使用Java序列化已经很长时间了。在我刚刚写的博客文章中,您可以找到与此主题相关的一些潜在有趣的东西。nemanjakovacevic.net/blog/english/2015/03/24/…–
Nemanja Kovacevic

1
是的,这不再是真正的问题。几年来,我只使用了Serializable(具有优化的readObject / writeObject实现)。实际上,仅几天前,我才查看了一些序列化对象的十六进制转储,并且感到满意,因为这样做并没有太浪费。
Reuben Scratton


11

如果出于存储目的需要序列化,但又希望避免Serializable接口引起的反射速度损失,则应该使用Externalizable接口显式创建自己的序列化协议。

如果正确实施,则可以与Parcelable的速度相匹配,并且可以说明不同版本的Android和/或Java平台之间的兼容性。

本文还可以解决这些问题:

Java中的Serializable和Externalizable有什么区别?

在旁注中,它也是许多基准测试中最快的序列化技术,击败了Kryo,Avro,Protocol Buffers和Jackson(json):

http://code.google.com/p/thrift-protobuf-compare/wiki/基准测试


7

如今,这种差异似乎并不那么明显,至少在您自己的活动之间运行它时,这种差异并不明显。

根据该网站上显示的测试,在最新设备(例如nexus 10),Parcelable的速度大约是10倍,而在旧设备(例如欲望Z),Parcelable的速度大约是17倍。

因此由您决定是否值得。

也许对于相对较小和简单的类,Serializable就可以,其余的,您应该使用Parcelable


我认为您说的对了,而且差距正在缩小,这是对的。但是我发现,就可封送处理的字节数组的大小而言,使用序列化可以更加高效,并且可以帮助避免TransactionTooLargeException。我很想听听您对此(我的)博客文章nemanjakovacevic.net/blog/english/2015/03/24/…的评论
Nemanja Kovacevic

好吧,您可以将巨大的内存消耗对象放在静态变量上,并在获取对象时将其设置为null(例如在onCreate上)。缺点是它不支持多进程,并且这样做有点脏。不知道是否要传递大位图。
Android开发人员

4

Parcelable主要与使用Binder基础结构的IPC有关,其中数据作为Parcels传递。

由于Android大部分(即使不是全部)IPC任务都依赖于Binder,因此在大多数地方(尤其是在框架中)实现Parcelable是有意义的,因为它允许在需要时将对象传递给另一个进程。它使对象“可运输”。

但是,如果您有一个非Android专用的业务层,该层广泛使用可序列化的对象来保存对象状态,并且只需要将它们存储到文件系统中,那么我认为可序列化就可以了。它可以避免使用可拆分样板代码。


您希望在哪些示例中将实际对象存储在saya文件系统中?为什么不简单地获取对象内容并将实际内容存储在文件中。以JSON为例,甚至以xml为例。您可以保存JSON或XML格式的对象,也可以保存POJO / Entity类型的对象,这些对象可以构造典型的数据对象,该数据对象主要由State以及该状态的getter和setter组成。这样,就无需为了存储对象而序列化对象,因为您只关心对象状态
Jonathan


1

我只是使用GSON->序列化为JSON字符串->从JSON字符串还原对象。


对于小型对象来说,这很好,但是当您有大量对象且每个对象都有很多属性时,就没有那么多了。
Nickmccomb '17

0

另外,Parcelable还提供了自定义实现,用户可以通过重写writeToParcel()来打包每个对象的机会,但是序列化没有此自定义实现,因为其传递数据的方式涉及JAVA反射API。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.