Android中Serializable和Parcelable序列化對象詳解

學習內容: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軟硬件環境上進行測試

  • LG Nexus 4 – Android 4.2.2
  • Samsung Nexus 10 – Android 4.2.2
  • HTC Desire Z – Android 2.3.3 

 結果如圖:

201622495741310.jpg (619×353)

性能差別:

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.固然實現後者也不是不行,關鍵在於咱們頭腦中的那一份思想。

相關文章
相關標籤/搜索