学习内容:

1.序列化的目的

2.Android中序列化的两种方式

3.Parcelable与Serializable的性能比较

4.Android中如何使用Parcelable进行序列化操作

5.Parcelable的工作原理

6.相关实例

1.序列化的目的

(1).永久的保存对象数据(将对象数据保存在文件当中,或者是磁盘中

(2).通过序列化操作将对象数据在网络上进行传输(由于网络传输是以字节流的方式对数据进行传输的.因此序列化的目的是将对象数据转换成字节流的形式)

(3).将对象数据在进程之间进行传递(Activity之间传递对象数据时,需要在当前的Activity中对对象数据进行序列化操作.在另一个Activity中需要进行反序列化操作讲数据取出)

(4).Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长(即每个对象都在JVM中)但在现实应用中,就可能要停止JVM运行,但有要保存某些指定的对象,并在将来重新读取被保存的对象。这是Java对象序列化就能够实现该功能。(可选择入数据库、或文件的形式保存)

(5).序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化.

(6).在Intent之间,基本的数据类型直接进行相关传递即可,但是一旦数据类型比较复杂的时候,就需要进行序列化操作了.

2.Android中实现序列化的两种方式

(1).Implements Serializable 接口 (声明一下即可)Serializable 的简单实例:

public class Person implementsSerializable{private static final long serialVersionUID = -7060210544600464481L;privateString name;private intage;publicString getName(){returnname;

}public voidsetName(String name){this.name =name;

}public intgetAge(){returnage;

}public void setAge(intage){this.age =age;

}

}

(2).Implements Parcelable 接口(不仅仅需要声明,还需要实现内部的相应方法)Parcelable的简单实例:

注:写入数据的顺序和读出数据的顺序必须是相同的.

public class Book implementsParcelable{privateString bookName;privateString author;private intpublishDate;publicBook(){

}publicString getBookName(){returnbookName;

}public voidsetBookName(String bookName){this.bookName =bookName;

}publicString getAuthor(){returnauthor;

}public voidsetAuthor(String author){this.author =author;

}public intgetPublishDate(){returnpublishDate;

}public void setPublishDate(intpublishDate){this.publishDate =publishDate;

}

@Overridepublic intdescribeContents(){return 0;

}

@Overridepublic void writeToParcel(Parcel out, intflags){

out.writeString(bookName);

out.writeString(author);

out.writeInt(publishDate);

}public static final Parcelable.Creator CREATOR = new Creator(){

@Overridepublic Book[] newArray(intsize){return newBook[size];

}

@OverridepublicBook createFromParcel(Parcel in){return newBook(in);

}

};publicBook(Parcel in){//如果元素数据是list类型的时候需要: lits = new ArrayList> in.readList(list);

//否则会出现空指针异常.并且读出和写入的数据类型必须相同.如果不想对部分关键字进行序列化,可以使用transient关键字来修饰以及static修饰.

bookName =in.readString();

author=in.readString();

publishDate=in.readInt();

}

}

我们知道在Java应用程序当中对类进行序列化操作只需要实现Serializable接口就可以,由系统来完成序列化和反序列化操作,但是在Android中序列化操作有另外一种方式来完成,那就是实现Parcelable接口.也是Android中特有的接口来实现类的序列化操作.原因是Parcelable的性能要强于Serializable.因此在绝大多数的情况下,Android还是推荐使用Parcelable来完成对类的序列化操作的.

3.Parcelable与Serializable的性能比较

首先Parcelable的性能要强于Serializable的原因我需要简单的阐述一下

1). 在内存的使用中,前者在性能方面要强于后者

2). 后者在序列化操作的时候会产生大量的临时变量,(原因是使用了反射机制)从而导致GC的频繁调用,因此在性能上会稍微逊色

3). Parcelable是以Ibinder作为信息载体的.在内存上的开销比较小,因此在内存之间进行数据传递的时候,Android推荐使用Parcelable,既然是内存方面比价有优势,那么自然就要优先选择.

4). 在读写数据的时候,Parcelable是在内存中直接进行读写,而Serializable是通过使用IO流的形式将数据读写入在硬盘上.

但是:虽然Parcelable的性能要强于Serializable,但是仍然有特殊的情况需要使用Serializable,而不去使用Parcelable,因为Parcelable无法将数据进行持久化,因此在将数据保存在磁盘的时候,仍然需要使用后者,因为前者无法很好的将数据进行持久化.(原因是在不同的Android版本当中,Parcelable可能会不同,因此数据的持久化方面仍然是使用Serializable)

速度测试:

测试方法:

1)、通过将一个对象放到一个bundle里面然后调用Bundle#writeToParcel(Parcel, int)方法来模拟传递对象给一个activity的过程,然后再把这个对象取出来。

2)、在一个循环里面运行1000 次。

3)、两种方法分别运行10次来减少内存整理,cpu被其他应用占用等情况的干扰。

4)、参与测试的对象就是上面的相关代码

5)、在多种Android软硬件环境上进行测试

LG Nexus 4 – Android 4.2.2

Samsung Nexus 10 – Android 4.2.2

HTC Desire Z – Android 2.3.3

结果如图:

性能差异:

Nexus 10

Serializable: 1.0004ms,  Parcelable: 0.0850ms – 提升10.16倍。

Nexus 4

Serializable: 1.8539ms – Parcelable: 0.1824ms – 提升11.80倍。

Desire Z

Serializable: 5.1224ms – Parcelable: 0.2938ms – 提升17.36倍。

由此可以得出: Parcelable 比 Serializable快了10多倍。

从相对的比较我们可以看出,Parcelable的性能要比Serializable要优秀的多,因此在Android中进行序列化操作的时候,我们需要尽可能的选择前者,需要花上大量的时间去实现Parcelable接口中的内部方法.

4.Android中如何使用Parcelable进行序列化操作

说了这么多,我们还是来看看Android中如何去使用Parcelable实现类的序列化操作吧.

Implements Parcelable的时候需要实现内部的方法:

1).writeToParcel 将对象数据序列化成一个Parcel对象(序列化之后成为Parcel对象.以便Parcel容器取出数据)

2).重写describeContents方法,默认值为0

3).Public static final Parcelable.CreatorCREATOR (将Parcel容器中的数据转换成对象数据) 同时需要实现两个方法:

3.1 CreateFromParcel(从Parcel容器中取出数据并进行转换.)

3.2 newArray(int size)返回对象数据的大小

因此,很明显实现Parcelable并不容易。实现Parcelable接口需要写大量的模板代码,这使得对象代码变得难以阅读和维护。具体的实例就是上面Parcelable的实例代码.就不进行列举了.(有兴趣的可以去看看Android中NetWorkInfo的源代码,是关于网络连接额外信息的一个相关类,内部就实现了序列化操作.大家可以去看看)

5.Parcelable的工作原理

无论是对数据的读还是写都需要使用Parcel作为中间层将数据进行传递.Parcel涉及到的东西就是与C++底层有关了.都是使用JNI.在Java应用层是先创建Parcel(Java)对象,然后再调用相关的读写操作的时候.就拿读写32为Int数据来说吧:

static jint android_os_Parcel_readInt(JNIEnv*env, jobject clazz){

Parcel* parcel =parcelForJavaObject(env, clazz);if (parcel !=NULL) {return parcel->readInt32();

}return 0;

}

调用的方法就是这个过程,首先是将Parcel(Java)对象转换成Parcel(C++)对象,然后被封装在Parcel中的相关数据由C++底层来完成数据的序列化操作.

status_t Parcel::writeInt32(int32_t val){returnwriteAligned(val);

}

templatestatus_t Parcel::writeAligned(T val) {

COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T))==sizeof(T));if ((mDataPos+sizeof(val)) <=mDataCapacity) {

restart_write:*reinterpret_cast(mData+mDataPos) =val;returnfinishWrite(sizeof(val));

}

status_t err=growData(sizeof(val));if (err == NO_ERROR) gotorestart_write;returnerr;

}

真正的读写过程是由下面的源代码来完成的.

status_t Parcel::continueWrite(size_t desired)

{//If shrinking, first adjust for any objects that appear//after the new data size.

size_t objectsSize =mObjectsSize;if (desired

objectsSize= 0;

}else{while (objectsSize > 0) {if (mObjects[objectsSize-1]

objectsSize--;

}

}

}if(mOwner) {//If the size is going to zero, just release the owner's data.

if (desired == 0) {

freeData();returnNO_ERROR;

}//If there is a different owner, we need to take//posession.

uint8_t* data = (uint8_t*)malloc(desired);if (!data) {

mError=NO_MEMORY;returnNO_MEMORY;

}

size_t* objects =NULL;if(objectsSize) {

objects= (size_t*)malloc(objectsSize*sizeof(size_t));if (!objects) {

mError=NO_MEMORY;returnNO_MEMORY;

}//Little hack to only acquire references on objects//we will be keeping.

size_t oldObjectsSize =mObjectsSize;

mObjectsSize=objectsSize;

acquireObjects();

mObjectsSize=oldObjectsSize;

}if(mData) {

memcpy(data, mData, mDataSize< desired ?mDataSize : desired);

}if (objects &&mObjects) {

memcpy(objects, mObjects, objectsSize*sizeof(size_t));

}//ALOGI("Freeing data ref of %p (pid=%d)\n", this, getpid());

mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);

mOwner=NULL;

mData=data;

mObjects=objects;

mDataSize= (mDataSize < desired) ?mDataSize : desired;

ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);

mDataCapacity=desired;

mObjectsSize= mObjectsCapacity =objectsSize;

mNextObjectHint= 0;

}else if(mData) {if (objectsSize

const spproc(ProcessState::self());for (size_t i=objectsSize; i(mData+mObjects[i]);if (flat->type ==BINDER_TYPE_FD) {//will need to rescan because we may have lopped off the only FDs

mFdsKnown = false;

}

release_object(proc,*flat, this);

}

size_t* objects =(size_t*)realloc(mObjects, objectsSize*sizeof(size_t));if(objects) {

mObjects=objects;

}

mObjectsSize=objectsSize;

mNextObjectHint= 0;

}//We own the data, so we can just do a realloc().

if (desired >mDataCapacity) {

uint8_t* data = (uint8_t*)realloc(mData, desired);if(data) {

mData=data;

mDataCapacity=desired;

}else if (desired >mDataCapacity) {

mError=NO_MEMORY;returnNO_MEMORY;

}

}else{if (mDataSize >desired) {

mDataSize=desired;

ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);

}if (mDataPos >desired) {

mDataPos=desired;

ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);

}

}

}else{//This is the first data. Easy!

uint8_t* data = (uint8_t*)malloc(desired);if (!data) {

mError=NO_MEMORY;returnNO_MEMORY;

}if(!(mDataCapacity == 0 && mObjects ==NULL&& mObjectsCapacity == 0)) {

ALOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired);

}

mData=data;

mDataSize= mDataPos = 0;

ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);

ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);

mDataCapacity=desired;

}returnNO_ERROR;

}

1).整个读写全是在内存中进行,主要是通过malloc()、realloc()、memcpy()等内存操作进行,所以效率比JAVA序列化中使用外部存储器会高很多

2).读写时是4字节对齐的,可以看到#define PAD_SIZE(s) (((s)+3)&~3)这句宏定义就是在做这件事情

3).如果预分配的空间不够时newSize = ((mDataSize+len)*3)/2;会一次多分配50%

4).对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不用重新new一个新对象。

6.相关实例

最后上一个例子..

首先是序列化的类Book.class

public class Book implementsParcelable{privateString bookName;privateString author;private intpublishDate;publicBook(){

}publicString getBookName(){returnbookName;

}public voidsetBookName(String bookName){this.bookName =bookName;

}publicString getAuthor(){returnauthor;

}public voidsetAuthor(String author){this.author =author;

}public intgetPublishDate(){returnpublishDate;

}public void setPublishDate(intpublishDate){this.publishDate =publishDate;

}

@Overridepublic intdescribeContents(){return 0;

}

@Overridepublic void writeToParcel(Parcel out, intflags){

out.writeString(bookName);

out.writeString(author);

out.writeInt(publishDate);

}public static final Parcelable.Creator CREATOR = new Creator(){

@Overridepublic Book[] newArray(intsize){return newBook[size];

}

@OverridepublicBook createFromParcel(Parcel in){return newBook(in);

}

};publicBook(Parcel in){//如果元素数据是list类型的时候需要: lits = new ArrayList> in.readList(list); 否则会出现空指针异常.并且读出和写入的数据类型必须相同.如果不想对部分关键字进行序列化,可以使用transient关键字来修饰以及static修饰.

bookName =in.readString();

author=in.readString();

publishDate=in.readInt();

}

}

第一个Activity,MainActivity

Book book = newBook();

book.setBookname("Darker");

book.setBookauthor("me");

book.setPublishDate(20);

Bundle bundle= newBundle();

bundle.putParcelable("book", book);

Intent intent= new Intent(MainActivity.this,AnotherActivity.class);

intent.putExtras(bundle);

第二个Activity,AnotherActivity

Intent intent =getIntent();

Bundle bun=intent.getExtras();

Book book= bun.getParcelable("book");

System.out.println(book);

总结:Java应用程序中有Serializable来实现序列化操作,Android中有Parcelable来实现序列化操作,相关的性能也作出了比较,因此在Android中除了对数据持久化的时候需要使用到Serializable来实现序列化操作,其他的时候我们仍然需要使用Parcelable来实现序列化操作,因为在Android中效率并不是最重要的,而是内存,通过比较Parcelable在效率和内存上都要优秀与Serializable,尽管Parcelable实现起来比较复杂,但是如果我们想要成为一名优秀的Android软件工程师,那么我们就需要勤快一些去实现Parcelable,而不是偷懒与实现Serializable.当然实现后者也不是不行,关键在于我们头脑中的那一份思想。

java parcelable list_Android中Serializable和Parcelable序列化对象详解相关推荐

  1. java的case_java中的switch case语句使用详解

    java中的switch case语句 switch-case语句格式如下: switch(变量){ case 变量值1: //; break; case 变量值2: //...; break; .. ...

  2. java Serializable和Externalizable序列化反序列化详解--转

    一.什么是序列化?   "对象序列化"(Object Serialization)是 Java1.1就开始有的特性. 简单地说,就是可以将一个对象(标志对象的类型)及其状态转换为字 ...

  3. java 全局变量_Javascript中的局部变量、全局变量的详解与var、let的使用区别

    前言 Javascript中的变量定义方式有以下三种方式: 1.直接定义变量,var与let均不写: a = 10; 2.使用var关键字定义变量 var a = 10; 3.使用let关键字定义变量 ...

  4. java访问mongodb_Java中获取MongoDB连接的方法详解

    首先是所需jar包,Maven中的配置如下: org.mongodb mongodb-driver 3.4.1 org.mongodb bson 3.4.1 org.springframework.d ...

  5. java integer == int_Java中int和Integer的区别详解

    1.Java 中的数据类型分为基本数据类型和复杂数据类型 int是前者>>integer 是后者(也就是一个类) 2.初始化时>> int i =1; Integer i= n ...

  6. java timer schedule_Java中Timer的schedule()方法参数详解

    timer.schedule(new MyTask(),long time1,long timer2); 今天算是彻底的搞懂了这个曾经让我为之头疼的方法.下面我就重点介绍一下: 第一个参数,是 Tim ...

  7. java 实例化list_java中List的用法和实例详解

    List的用法 List包括List接口以及List接口的所有实现类.因为List接口实现了Collection接口,所以List接口拥有Collection接口提供的所有常用方法,又因为List是列 ...

  8. JavaScript中的this的指代对象详解

    在javascript里面,this是一个特殊的对象,它不像其他编程语言那样,是存储在实例中的值,直接指向此实例. 而是作为一个单独的指针,在不同的情况之下,指向不同的位置,这也是为什么我们会将它搞混 ...

  9. Java Web学习总结(5)——HttpServletResponse对象详解

    一.HttpServletResponse常见应用--生成验证码 1.1.生成随机图片用作验证码 生成图片主要用到了一个BufferedImage类, 生成随机图片范例:

最新文章

  1. Sql Server函数全解三数据类型转换函数和文本图像函数
  2. jQuery选择器之可见性过滤选择器
  3. 《JavaScript 每周导读》【第一期】
  4. 怎么利用Excel统计各分数段的人数?(亲测sum函数可用)
  5. 《人民日报》专访姚期智院士:AI是历史性的起跑线机遇
  6. 安装redis提示[test] error 2_技术干货分享:一次flask+redis的微服务实战
  7. 如何在Java的特定范围内生成随机整数? [英]How do I generate random integers within a specific range in Java?
  8. java线程中Exchanger使用
  9. 日期格式 java_Java时间日期格式转换
  10. BLE芯片DA145XX系列:GPIO特殊配置
  11. 二 不插SIM卡的GPRS模组-AIR202通过AT指令链接阿里云
  12. 服务器系统试用,苹果“雪豹”服务器操作系统试用安装篇
  13. Qt MDI Window开发
  14. 电脑仙人掌机器人作文_仙人掌作文之电脑仙人掌机器人的童话作文
  15. 案例分析: 全球顶尖的物流公司Schenker采用SAPERION
  16. 第十三届蓝桥杯大赛软件赛决赛(Java 大学C组)
  17. 《自然语言处理入门》何晗阅读笔记—第1章:自然语言处理基础概念
  18. olivettifaces人脸识别之思考
  19. tarjan算法讲解
  20. Py4JJavaError /Library/Frameworks/Python.framework/Versions/3.5/bin/python3: error=2, No such file

热门文章

  1. 量化交易:大盘拟合稳定突破策略
  2. 【转】 数学建模竞赛的准备、技巧、选题、写作等各方面得总结
  3. canvas生成姓氏头像,上传七牛后传给后端
  4. 【iOS】跳转邮箱发送邮件,支持系统内多个邮箱的方案 swift 实现
  5. 新金融分布式架构之SOFAStack解决方案
  6. java常用知识点系列-1
  7. React项目,从详情页返回列表页时,保存数据并返回到原来的位置
  8. python里enumerate函数_python中的enumerate函数
  9. 不中听的意见,不想采纳怎么办?
  10. 怎么将项目里的appcompat_v7_10文件去掉