学习内容:android
1.序列化的目的数据库
2.Android中序列化的两种方式网络
3.Parcelable与Serializable的性能比较session
4.Android中如何使用Parcelable进行序列化操做app
5.Parcelable的工做原理ide
6.相关实例性能
1.序列化的目的学习
(1).永久的保存对象数据(将对象数据保存在文件当中,或者是磁盘中测试
(2).经过序列化操做将对象数据在网络上进行传输(因为网络传输是以字节流的方式对数据进行传输的.所以序列化的目的是将对象数据转换成字节流的形式)ui
(3).将对象数据在进程之间进行传递(Activity之间传递对象数据时,须要在当前的Activity中对对象数据进行序列化操做.在另外一个Activity中须要进行反序列化操做讲数据取出)
(4).Java平台容许咱们在内存中建立可复用的Java对象,但通常状况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长(即每一个对象都在JVM中)但在现实应用中,就可能要中止JVM运行,但有要保存某些指定的对象,并在未来从新读取被保存的对象。这是Java对象序列化就可以实现该功能。(可选择入数据库、或文件的形式保存)
(5).序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化.
(6).在Intent之间,基本的数据类型直接进行相关传递便可,可是一旦数据类型比较复杂的时候,就须要进行序列化操做了.
2.Android中实现序列化的两种方式
(1).Implements Serializable 接口 (声明一下便可)
Serializable 的简单实例:
public class Person implements Serializable{ private static final long serialVersionUID = -7060210544600464481L; private String name; private int age; public String getName(){ return name; } public void setName(String name){ this.name = name; } public int getAge(){ return age; } public void setAge(int age){ this.age = age; } }
(2).Implements Parcelable 接口(不只仅须要声明,还须要实现内部的相应方法)
Parcelable的简单实例:
注:写入数据的顺序和读出数据的顺序必须是相同的.
public class Book implements Parcelable{ private String bookName; private String author; private int publishDate; public Book(){ } public String getBookName(){ return bookName; } public void setBookName(String bookName){ this.bookName = bookName; } public String getAuthor(){ return author; } public void setAuthor(String author){ this.author = author; } public int getPublishDate(){ return publishDate; } public void setPublishDate(int publishDate){ this.publishDate = publishDate; } @Override public int describeContents(){ return 0; } @Override public void writeToParcel(Parcel out, int flags){ out.writeString(bookName); out.writeString(author); out.writeInt(publishDate); } public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){ @Override public Book[] newArray(int size){ return new Book[size]; } @Override public Book createFromParcel(Parcel in){ return new Book(in); } }; public Book(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软硬件环境上进行测试
结果如图:
性能差别:
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.Creator<T>CREATOR (将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){ return writeAligned(val); } template<class t=""> status_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<t*>(mData+mDataPos) = val; return finishWrite(sizeof(val)); } status_t err = growData(sizeof(val)); if (err == NO_ERROR) goto restart_write; return err; } 真正的读写过程是由下面的源代码来完成的. 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 < mDataSize) { if (desired == 0) { objectsSize = 0; } else { while (objectsSize > 0) { if (mObjects[objectsSize-1] < desired) break; objectsSize--; } } } if (mOwner) { // If the size is going to zero, just release the owner's data. if (desired == 0) { freeData(); return NO_ERROR; } // If there is a different owner, we need to take // posession. uint8_t* data = (uint8_t*)malloc(desired); if (!data) { mError = NO_MEMORY; return NO_MEMORY; } size_t* objects = NULL; if (objectsSize) { objects = (size_t*)malloc(objectsSize*sizeof(size_t)); if (!objects) { mError = NO_MEMORY; return NO_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 < mObjectsSize) { // Need to release refs on any objects we are dropping. const sp<ProcessState> proc(ProcessState::self()); for (size_t i=objectsSize; i<mObjectsSize; i++) { const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(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; return NO_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; return NO_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; } return NO_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 implements Parcelable{ private String bookName; private String author; private int publishDate; public Book(){ } public String getBookName(){ return bookName; } public void setBookName(String bookName){ this.bookName = bookName; } public String getAuthor(){ return author; } public void setAuthor(String author){ this.author = author; } public int getPublishDate(){ return publishDate; } public void setPublishDate(int publishDate){ this.publishDate = publishDate; } @Override public int describeContents(){ return 0; } @Override public void writeToParcel(Parcel out, int flags){ out.writeString(bookName); out.writeString(author); out.writeInt(publishDate); } public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){ @Override public Book[] newArray(int size){ return new Book[size]; } @Override public Book createFromParcel(Parcel in){ return new Book(in); } }; public Book(Parcel in){ //若是元素数据是list类型的时候须要: lits = new ArrayList<?> in.readList(list); 不然会出现空指针异常.而且读出和写入的数据类型必须相同.若是不想对部分关键字进行序列化,可使用transient关键字来修饰以及static修饰. bookName = in.readString(); author = in.readString(); publishDate = in.readInt(); } }
第一个Activity,MainActivity
Book book = new Book(); book.setBookname("Darker"); book.setBookauthor("me"); book.setPublishDate(20); Bundle bundle = new Bundle(); 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.固然实现后者也不是不行,关键在于咱们头脑中的那一份思想。