1. 程式人生 > >Android之IPC3————序列化

Android之IPC3————序列化

Android之IPC3————序列化

文章目錄

一.前言

說起序列化,大家應該都不會陌生,在Android中的應用也比較多,特別是在Activity直接傳遞物件時。就需要使用序列化,我一般是使用Serialization對物件進行序列化,然後進行傳遞。而在上篇文章中,在使用AIDL時,對跨程序傳遞的物件也進行了序列化,當時我們使用的是Parcelable。它也是一種序列化的方法。

在這篇文章裡,我分別介紹一下兩者的使用以及區別

二.序列化

1.什麼是序列化

序列化 (Serialization)將物件的狀態資訊轉換為可以儲存或傳輸的形式的過程。在序列化期間,物件將其當前狀態寫入到臨時或永續性儲存區。以後,可以通過從儲存區中讀取或反序列化物件的狀態,重新建立該物件。

簡單來說就是講物件進行儲存,在下次使用時可以順利還原該物件。

2.序列化儲存的內容

物件是類的一個例項,一個類中包含變數和函式兩部分。同一個類的不同物件只是變數不同,所以序列化是隻儲存物件的變數部分。同樣,由於靜態變數是由一個類的各個物件共用的,所以序列化過程中也不儲存。

3.序列化的作用

序列化的用途主要有三個:

  • 物件的持久化,物件持久化是指延長物件的存在時間,比如通過序列化功能,將物件儲存在檔案中,就可以延長物件的存在時間,在下次執行程式是再恢復該物件
  • 物件複製,通過序列化後,將物件儲存在記憶體中,可以在用過次資料得到多個物件的副本
  • 物件傳輸,通過序列化,可以通過網路傳遞物件,以及跨程序通訊。

三.Serialization

1.實現介面

使用Serialization進行序列化,是比較簡單的一件事,只要將需要序列化的類實現Serialization介面,並宣告一個SerialVersionUID即可,甚至SerialVersionUID也不是必須的。沒有它依然可以序列化,只是對反序列化造成影響

下面我們宣告一個類,並將它實現Serialization介面

public class User implements Serializable {


    private int userId;
    private String userName;
    private boolean isMale;

    public User(int UserId,String UserName,boolean IsMale){
        userId = UserId;
        userName = UserName;
        isMale = IsMale;
    }
    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public boolean isMale() {
        return isMale;
    }

    public void setMale(boolean male) {
        isMale = male;
    }
}

2.序列化和反序列化

public class Main {

    public static void main(String[] args) {
        //序列化過程
        User user = new User(1,"shucheng",true);
        try {
            ObjectOutputStream  out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
            out.writeObject(user);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


        //反序列化過程
        try {
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
            User newUser  = (User)in.readObject();
            in.close();

            System.out.println(newUser.getUserName());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

上面就是採用Serializable方式實現序列化物件的典型過程。恢復後的物件和之前的物件內容一樣,但並不是同樣的物件

3.SerialVersionUID的作用

SerialVersionUID是序列化的機制判斷類的版本一致性的。進行反序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常,即是InvalidCastException。

serialVersionUID有兩種顯示的生成方式:
一是預設的1L,比如:private static final long serialVersionUID = 1L;
二是根據類名、介面名、成員方法及屬性等來生成一個64位的雜湊欄位,比如:
private static final long serialVersionUID = xxxxL;

當序列化前後的SerialVersionUID發生變化,對應的類發生變化時,有以下幾種情況

情況一:假設User類序列化之後,從A端傳輸到B端,然後在B端進行反序列化。在序列化Person和反序列化Person的時候,A端和B端都需要存在一個相同的類。如果兩處的serialVersionUID不一致,會產生什麼錯誤呢?
結果:結果報錯

情況二:假設兩處serialVersionUID一致,如果A端增加一個欄位,然後序列化,而B端不變,然後反序列化,會是什麼情況呢?
結果:執行序列化,反序列化正常,但是A端增加的欄位丟失(被B端忽略)。

情況三:假設兩處serialVersionUID一致,如果B端減少一個欄位,A端不變,會是什麼情況呢?
結果:序列化,反序列化正常,B端欄位少於A端,A端多的欄位值丟失(被B端忽略)。

情況四:假設兩處serialVersionUID一致,如果B端增加一個欄位,A端不變,會是什麼情況呢?
結果:反序列化正常,B端新增加的int欄位被賦予了預設值0。

四.Parcelable

前面介紹了序列化和Java中使用Serializable來實現序列化。我們繼續來看android中的序列化Parcelable

1.為什麼使用Parcelable

在Android中是可以使用Serializable進行序列化,而且使用起來也很簡單,為什麼Android要自己在實現一個Parcelable方法。因為Serializable是Java中的序列化方法,雖然很簡單但是開銷很大。序列化和反序列化中需要大量的I/O操作。

Parcelable是Android中序列化的實現,它的缺點是使用起來稍微麻煩些,但是效率很高,這是Android推薦的序列化方式。Parcelable主要作用在記憶體序列化上。所以如果用了進行Intent和Binder傳輸,建議使用Parcelable。如果用來將物件序列化到本地或者用來進行網路傳輸時,還是推薦使用Serializable。

2.使用

以上一篇文章的序列化的類為例,在裡面也為其中的方法進行了註釋。

public class Book implements Parcelable {

    private String name;
    private int price;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "書名:"+name +",價格"+price;
    }

    public Book() {
    }

    /**
     * 返回當前物件的描述內容,如果含有檔案描述符,返回1,否則返回0
     * @return
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * 將當前物件寫入序列化結構中
     * @param dest
     * @param flags:有兩種0/1 為1時表示當前物件需要作為返回值返回,不能立即釋放資源,大部分情況下為0
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(price);
    }

    /**
     * 從序列化後的物件中建立原始物件
     * @param in
     */
    protected Book(Parcel in) {
        name = in.readString();
        price = in.readInt();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {

        /**
         * 從序列化後的物件中建立原始物件
         * @param in
         * @return
         */
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        /**
         * 建立指定長度的原始物件陣列
         * @param size
         * @return
         */
        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
}


五.參考資料

《Android藝術開發探索》
Java序列化(Serialization)的理解
Java 的序列化 (Serialization)教程
java類中serialversionuid 作用