关闭

Serializable和Parcelable的再次回忆

标签: androidSerializabParcelable序列化
3517人阅读 评论(3) 收藏 举报
分类:

自己开发Android也有些时间了,Serializable和Parcelable遇到过不止一次了。但是每次别人问起具体的内容自己偏偏记得不是很清晰。因为某些原因再次梳理一下,以文章的形式给自己存储下来。温故而知新~!~!

序列化和反序列化几乎是工程师们每天都要面对的事情,但是要精确掌握这两个概念并不容易:一方面,它们往往作为框架的一部分出现而湮没在框架之中;另一方面,它们会以其他更容易理解的概念出现,例如加密、持久化。然而,序列化和反序列化的选型却是系统设计或重构一个重要的环节,在分布式、大数据量系统设计里面更为显著。恰当的序列化协议不仅可以提高系统的通用性、强健性、安全性、优化系统性能,而且会让系统更加易于调试、便于扩展。总而言之搞懂序列化是很重要滴。

序列化

将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。

反序列化

就是读取序列化后保存在存储区的序列化信息或反序列化对象的状态,重新创建该对象。

序列化和反序列化通常是并存的,他们的关系就像进行加密和解密操作一样。前者需要相同的序列化方式,后者需要知道秘钥。

Android中将对象序列化的方式有两种Serializable和Parcelable这两个接口都可以完成。Serializable是Java自带的序列化方法,而Android原生的序列化为Parcelable。这并不意味着在Android中可以抛弃Serialable,只能说在Android中Parcelable方法实现序列化更有优势。下边我们可以具体来看看这两个接口实现。(PS:对象参加序列化的时候其类的结构是不能发生变化的,以下Demo都是在此基础上演示)

Serializable

我们先看一个实现Serializable接口的类:

public class User implements Serializable {
    private static final long serialVersionUID = -4454266436543306544L;
    public int userId;
    public String userName;
    public User(int userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }
}

Serializable是java提供的一个序列化接口,它是一个空接口,专门为对象提供标准的序列化和反序列化操作,使用Serializable来实现序列化相当简单,只需要在类的声明中指定一个类似下面的标示即可自动实现默认的序列化过程(serialVersionUID可自动生成也可自己写,如0L,1L,2L…)。

private static final long serialVersionUID = -4454266436543306544L;

通过Serializable方式来实现对象的序列化,实现起来非常简单,几乎素有工作都被系统自动完成。如果进行对象的序列化和反序列化也非常简单,只需要采用ObjectOutputStream和ObjectInputStream即可轻松实现。下面举个简单的列子:

//序列化过程
User user = new User(1, "Tom");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();
//反序列化过程
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
User newuser = (User) in.readObject();
in.close();

上述代码采用Serializable方式序列化对象的典型过程,只需要把实现Serializable接口的User对象写到文件中就可以快速恢复了,恢复后的对象newUser和User的内容完全一致,但是两个并不是同一个对象。

serialVersionUID

上面提到,想让一个对象实现序列化,只需要这个类实现Serializable接口并声明一个serialVersionUID即可,实际上,甚至这个serialVersionUID也不是必需的,我们不声明这个serialVersionUID同样可以实现序列化,但是这将会对反序列化过程产生影响,具体会产生什么影响呢?

解决这个问题前我们先提一个问题,为什么需要serialVersionUID呢?

因为静态成员变量属于类不属于对象,不会参与序列化过程,使用transient关键字标记的成员变量也不参与序列化过程。 (PS:关键字transient,这里简单说明一下,Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的)

这个时候又有一个疑问serialVersionUID是静态成员变量不参与序列化过程,那么它的存在与否有什么影响呢?

具体过程是这样的:序列化操作的时候系统会把当前类的serialVersionUID写入到序列化文件中,当反序列化时系统会去检测文件中的serialVersionUID,判断它是否与当前类的serialVersionUID一致,如果一致就说明序列化类的版本与当前类版本是一样的,可以反序列化成功,否则失败。

InvalidClassException

接下来我们回答声明serialVersionUID对进行序列化有啥影响?

如果不手动指定serialVersionUID的值,反序列化时当前类有所改变,比如增加或者删除了某些成员变量,那么系统就会重新计算当前类的hash值并且把它赋值给serialVersionUID,这个时候当前类的serialVersionUID就和序列化的数据中的serialVersionUID不一致,于是反序列化失败。所以我们手动指定serialVersionUID的值能很大程度上避免了反序列化失败。

以上就是自己对Serializable的认识,下边来看看Parcelable相关的知识~!~!

Parcelable

我们先看一个使用Parcelable进行序列化的例子:

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public Book() {
    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }
    //从序列化后的对象中创建原始对象
    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        //从序列化后的对象中创建原始对象
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }
        //指定长度的原始对象数组
        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
    //返回当前对象的内容描述。如果含有文件描述符,返回1,否则返回0,几乎所有情况都返回0
    @Override
    public int describeContents() {
        return 0;
    }
    //将当前对象写入序列化结构中,其flags标识有两种(1|0)。
    //为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况下都为0.
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    @Override
    public String toString() {
        return "[bookId=" + bookId + ",bookName='" + bookName + "']";
    }
}

虽然Serializable可以将数据持久化在磁盘,但其在内存序列化上开销比较大(PS:Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC),而内存资源属于android系统中的稀有资源(android系统分配给每个应用的内存开销都是有限的),为此android中提供了Parcelable接口来实现序列化操作,在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。

Parcelable内部包装了可序列化的数据,可以在Biander中自由的传输,从代码中可以看出,在序列化过程中需要实现的功能有序列化,反序列化和内容描述。序列化功能是由writetoParcel方法来完成,最终是通过Parcel中的一系列write方法来完成的。反序列化功能是由CREATOR方法来完成,其内部标明了如何创建序列化对象和数组,并通过Parcel的一系列read方法来完成反序列化过程(PS:write和read的顺序必须一致~!);内容描述功能是有describeContents方法来完成,几乎所有情况下这个方法都应该返回0,仅当当前对象中存在文件描述符时,此方法返回1.

系统已经给我们提供了许多实现了Parcelable接口类,他们都是可以直接序列化的,比如Intent,Bundle,Bitmap等,同事List和Map也支持序列化,提前是他们里面的每个元素都是可以序列化的。

总结

既然Parcelable和Serializable都能实现序列化并且都可以用于Intent间传递数据,那么二者改如果选择呢?Serializable是Java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量的I/O操作。而Parcelable是Android中序列化方法,因为更适合于在Android平台上,它的缺点就是使用起来比较麻烦,但是它的效率很高,这是Android推荐的序列化方法,因为我们要首选Parcelable。Parcelable主要用于内存序列化上,通过Parcelable将对象序列化到存储设备中或者将对象序列化后通过网络传输也都是可以的,但是这个过程稍显复杂,因此在这两种情况下建议使用Serializable。

更多文章–>我的个人博客下雨天要逛街

文章部分摘自任玉刚的《Android开发艺术探索》

0
0
查看评论

Android Serializable与Parcelable原理与区别

一、序列化、反序列化是什么? (1) 名词解释 对象的序列化 : 把Java对象转换为字节序列并存储至一个储存媒介的过程。 对象的反序列化:把字节序列恢复为Java对象的过程。 (2) 序列化详细解释 对象的序列化涉及三个点关键点:Java对象、字节序列、存储。 1. Java对象的组成? Jav...
  • love_world_
  • love_world_
  • 2014-03-26 07:25
  • 4579

序列化与反序列化之Parcelable和Serializable浅析

转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/52665164 出自【zejian的博客】 本篇小部分内容摘自android开发艺术探索   在日常的应用开发中,我们可能需要让某些对象离开内存空间,...
  • javazejian
  • javazejian
  • 2016-09-26 15:50
  • 8573

浅谈 Parcelable与Serializable的区别

parcelable与serializable都是序列化的操作,那么我们先看 一下为何要使用序列化!1)永久性保存对象,保存对象的字节序列到本地文件中;2)通过序列化对象在网络中传递对象;3)通过序列化在进程间传递对象。 我们在项目中最经常遇到的情况就是,我们需要在activity或者servic...
  • yuekongxia01
  • yuekongxia01
  • 2015-12-14 09:54
  • 524

Android Intent传递Parcelable和Serializable对象原理分析

Android Intent传递Parcelable和Serializable对象原理分析  当我们需要打开一个Activity的时候,经常使用Intent去传递一些数据给将要打开的Activity。这些数据类型中经常会包含类类型,但是不能什么类都可以通过Intent传递的,必须是实现了Parcel...
  • u012341052
  • u012341052
  • 2017-03-30 11:40
  • 699

activity之间传值,解决:Parcelable encountered IOException writing serializable object异常

今天写代码的时候遇到了两个activity之间需要传数据,本来是一件非常简单的事情 平常都是使用 intent.putExtra(type,value);就没问题了。 传对象的话就需要实现序列化接口了就行了。 为什么要实现序列化接口,你可以当这个是android的一个机制,就是得这么办,不这么...
  • qq_33667176
  • qq_33667176
  • 2017-04-14 14:21
  • 3201

Serializable和Parcelable应该懂得的几个场景

1. Serialable接口      对于Serializable,类只需要实现Serializable接口,并提供一个序列化版本id(serialVersionUID)即可。当然这其实远远不够,下面对Serializable更深一点。 ...
  • u010794180
  • u010794180
  • 2015-02-13 17:22
  • 2365

Android那点事-系列之(一)Parcelable和Serializable的区别与使用

大家好,我是Bin,今天为大家分享一下Android中**Parcelable**和**Serializable**的区别与使用。 大家都知道,这两个都是和**序列化**相关的接口,那么我先带大家普及一下有关序列化的基础知识。 一、序列化、反序列化是什么? 对象的序列化:Java对象转化 为字节流的...
  • iot1515300705
  • iot1515300705
  • 2015-04-20 16:28
  • 721

[Android]Parcelable encountered IOException writing serializable object (name = xxx)

Activity之间通过Intent传递值,支持基本数据类型和String对象及它们的数组对象byte、byte[]、char、char[]、boolean、boolean[]、short、short[]、int、int[]、long、long[]、float、float[]、double、doub...
  • SunnyYoona
  • SunnyYoona
  • 2014-10-20 16:25
  • 2662

Parcelable encountered IOException writing serializable 问题解决

遇到这个问题已经好几天,在网上查资料看代码,搞了好久,今天又拿出来,在简书上看到一个大神的文章,终于解决掉了; 地址:http://www.jianshu.com/p/3da56fdca7c8
  • sinat_32089827
  • sinat_32089827
  • 2016-04-26 12:02
  • 1938

Parcelable和Serializable的区别:

Parcelable和Serializable的区别:     android自定义对象可序列化有两个选择一个是Serializable和Parcelable     一、对象为什么需要序列化       &...
  • salahelx
  • salahelx
  • 2015-09-16 10:20
  • 330
    个人资料
    • 访问:290396次
    • 积分:4004
    • 等级:
    • 排名:第9346名
    • 原创:329篇
    • 转载:17篇
    • 译文:10篇
    • 评论:35条
    博客专栏
    最新评论