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

Posted

技术标签:

【中文标题】使用 Parcelable 而不是序列化对象的好处【英文标题】:Benefit of using Parcelable instead of serializing object 【发布时间】:2011-07-29 20:53:45 【问题描述】:

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

【问题讨论】:

【参考方案1】:

来自“Pro Android 2”

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

也意识到Android提供了两种机制让你通过 数据到另一个 过程。第一种是使用意图将捆绑包传递给活动, 第二个是通过一个 可打包到服务。这两种机制不可互换 不应该是 使困惑。也就是说, Parcelable 并不意味着传递给 活动。如果你想开始 一个活动并传递一些数据,使用一个包。 Parcelable 的意思是 仅用作 AIDL 定义。

【讨论】:

Reto Meier 的专业 Android 2 应用程序开发amazon.com/… 第二段不正确,你可以将一个Parcelable作为参数传递给一个使用bundle的activity... 当我序列化我的对象时,我创建了一个getBundle 方法,然后从writeToParcel 调用它作为dest.writeBundle(getBundle()); 并且我在对象中自动拥有这两个选项。这里提到了一些有趣的活动对象 Parcel 功能:developer.android.com/reference/android/os/Parcel.html @lxx :我想知道为什么需要通过 bundle 将一个可打包的对象传递给活动。 IMO,如果您这样做,您将不必要地添加更多级别的序列化,仅此而已。 Philippe Breault 对此做了一篇不错的文章,还添加了性能测试。 developerphil.com/parcelable-vs-serializable【参考方案2】:

Serializable 在 Android 上运行速度慢得可笑。事实上,边界线在很多情况下是无用的。

ParcelParcelable 非常快,但它的documentation 表示您不能将它用于通用序列化到存储,因为不同版本的 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++;
        
    


【讨论】:

使用你的这个自定义类和只实现 Externalizable 接口并做同样的事情有什么区别? Bundle 也会对字段名进行序列化......它不适合数千个对象。 抱歉,发明了另一个序列化器糟透了 - 现在还有另一个“Parcelable”需要处理。有很多可供选择的库(不同之处在于库经过审查、测试并使用其他人使用的格式):ProtocolBuffers、JSON、XML 等。遗憾的是,Android 库在这方面真的很烂. 我不认为开头的句子是真的(5年后)。很长一段时间以来,我一直在使用 java 序列化,没有任何问题。你可以在我刚刚写的博客文章中找到一些关于这个主题的潜在有趣的东西。 nemanjakovacevic.net/blog/english/2015/03/24/… 是的,这不再是一个真正的问题了。几年来,我只使用了 Serializable(具有优化的 readObject/writeObject 实现)。事实上,就在几天前,我查看了一些序列化对象的十六进制转储,并且很满意它并没有太浪费。【参考方案3】:

看看Parcelable 比 Serializable 有多快。


来自WHY WE LOVE PARCELABLE


来自Parcelable vs Serializable

【讨论】:

【参考方案4】:

如果您需要序列化用于存储目的,但又想避免 Serializable 接口导致的反射速度损失,您应该使用 Externalizable 显式创建自己的序列化协议界面。

如果实施得当,这与 Parcelable 的速度相匹配,并且还考虑了不同版本的 Android 和/或 Java 平台之间的兼容性。

这篇文章也可以澄清一些事情:

What is the difference between Serializable and Externalizable in Java?

顺便说一句,它也是许多基准测试中最快的序列化技术,击败了 Kryo、Avro、Protocol Buffers 和 Jackson (json):

http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

【讨论】:

【参考方案5】:

现在看来,这种差异并不那么明显,至少当您在自己的活动之间运行它时不会那么明显。

根据this website 上显示的测试,Parcelable 在最新设备(如 nexus 10)上快了约 10 倍,在旧设备上快了约 17 倍(如欲望 Z)

因此,是否值得由您决定。

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

【讨论】:

我认为你说的差价正在缩小是对的。但是我发现使用可序列化的字节数组的大小可以更有效,并且可以帮助避免 TransactionTooLargeException。我很想在这篇(我的)博文nemanjakovacevic.net/blog/english/2015/03/24/… 上听到你的 cmets 好吧,你可以把巨大的内存消耗对象放在一个静态变量上,并在你获取它时将它设置为空(例如在 onCreate 上)。缺点是不支持多进程,做起来有点脏。想知道如果你想传递一个大的位图是不是这样。【参考方案6】:

Parcelable 主要与使用Binder 基础架构的IPC 相关,其中数据以Parcels 传递。

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

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

【讨论】:

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

基于本文http://www.mooproductions.org/node/6?page=5 Parcelable 应该更快。

文章中没有提到,我不认为可序列化对象可以在 AIDL 中用于远程服务。

【讨论】:

【参考方案8】:

我只使用 GSON -> 序列化为 JSON 字符串 -> 从 JSON 字符串恢复对象。

【讨论】:

这对于小对象来说很好,当你有大型对象数组并且每个对象都有很多属性时就不那么好了。【参考方案9】:

Parcelable 还提供了自定义实现,用户有机会通过覆盖 writeToParcel() 来打包他的每个对象,但是序列化并没有这种自定义实现,因为它传递数据的方式涉及 JAVA 反射 API。

【讨论】:

以上是关于使用 Parcelable 而不是序列化对象的好处的主要内容,如果未能解决你的问题,请参考以下文章

android中用Intent传数据,如果用传递的是一个类,就将类实现Parcelable接口

Kotlin用@Parcelize实现序列化Parcelable

Kotlin用@Parcelize实现序列化Parcelable

Android中Serializable和Parcelable序列化对象详解

Serializable Parcelable 问题:RuntimeException: Parcelable 在写入可序列化对象时遇到 IOException

X 类不是抽象的,没有实现 android.os.Parcelable 中定义的 fun writeToParcel()