Android IPC機制(一):序列化與反序列化
一、前言
對于Android開發者來說,IPC機制肯定不陌生,而作為Android的進階也必須掌握IPC機制。所謂IPC機制,即進程間通訊(Inter-Process Communication)。我們的應用有時候出于業務需要,可能是多進程的,而由于不同進程是不共享一個內存池的,所以進程之間不能直接通訊,而要通過一些特別的機制才能通訊,所以IPC機制是解決進程間通訊的一個方案。為了熟練掌握Android的IPC機制,我們先從基本的序列化與反序列化說起。
二、序列化與反序列化
由于在系統底層,數據的傳輸形式是簡單的字節序列形式傳遞,即在底層,系統不認識對象,只認識字節序列,而為了達到進程通訊的目的,需要先將數據序列化,而序列化就是將對象轉化字節序列的過程。相反地,當字節序列被運到相應的進程的時候,進程為了識別這些數據,就要將其反序列化,即把字節序列轉化為對象。有了以上理解,接下來我們認識兩個用于序列化和反序列化的接口:Serializable接口和Parcelable接口。
三、Serializable接口
Java提供了一個序列化接口,serialable接口,該接口在文檔中定義如下:Marks classes that can be serialized by ObjectOutputStream and deserialized by ObjectInputStream.從這句話可看出,該接口只是標記了當前類是可以序列化的,是一個空接口,僅僅提供了標志功能,具體的序列化與反序列化操作是由ObjectOutputStream和ObjectInputStream完成的。
繼續讀文檔,發現該接口要求我們在實現了該接口的類中聲明如下的一個變量:
private static final long serialVersionUID= 1L;
這個變量有什么用呢?試想一下,如果沒有手動指定該值,一開始序列化了classA,得到文件A,接著對classA的內部結構更改,比如添加了一個新的變量,那么此時反序列化則會失敗,因為實際上系統在序列化的時候,會自動計算出一個serialVersionUID值,并保存在已經序列化好的數據中,此時修改了classA,那么反序列化的時候系統就會重新計算一個新的serialVersionUID值,那么兩個值就會不相等,就會反序列化失敗。所以,手動指定一個值,能很大程度上保存數據,防止數據丟失。
接下來,我們來看一下序列化和反序列化的具體步驟:
·對象的序列化:
(1)實例化一個對象輸出流:ObjectOutputStream,該對象輸出流可以包裝一個輸出流,比如文件輸出流。
(2)使用ObjectOutputStream.writeObject(obj)進行寫對象。
·對象的反序列化:
(1)實例化一個對象輸入流:ObjectInputStream,該對象輸入流可以包裝一個輸入流,比如文件輸入流。
(2)使用ObjectInputStream.readObject(obj)進行讀對象。
以下是一個實現序列化與反序列化的范例:
①User類,被序列化的類:
package com.chenyu.serialable;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
public int id;
public String username;
public String email;
public User(int id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
}
}</code></pre>
②Test測試類,測試序列化與反序列化是否成功:
package com.chenyu.serialable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Test {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
//實例化User類
User user =new User(1,"TestName","example@126.com");
//序列化過程
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.txt"));
objectOutputStream.writeObject(user);
objectOutputStream.close();
System.out.println("序列化成功!");
//反序列化過程
ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream("test.txt"));
User newUser = (User) objectInputStream.readObject();
objectInputStream.close();
System.out.println("反序列化成功!");
System.out.println("ID:"+newUser.id+" username:"+newUser.username+" Email:"+newUser.email);
}
}</code></pre>
運行Test.java,得到如下結果:

Paste_Image.png
注意:靜態成員變量屬于類,而不是對象,所以不會參與序列化;使用transient關鍵字標記的成員變量不參與序列化過程。
四、Parcelable接口
接下來我們要說的是Parcelable接口,該接口是Android提供的新的序列化方式。首先,先看官方文檔對該接口的描述:Interface for classes whose instances can be written to and restored from a Parcel. Classes implementing the Parcelable interface must also have a static field called CREATOR, which is an object implementing the Parcelable.Creator interface.
除了實現該接口的方法外,還需創建一個名叫CREATOR的靜態對象,該對象實現了一個Parcelable.Creator的匿名內部類。以下是官方文檔的一個類實現Parcelable接口的典型例子:
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}</code></pre>
下面介紹一下以上各個方法的作用:
①writeToParcel(Parcel out,int flags):將當前對象寫入序列化結構之中。
②createFromParcel(Parcel in):從序列化后的對象中創建原始對象
③newArray(int size):創建指定長度的原始對象數組
④MyParcelable(Parcel in):從序列化后的對象中創建原始對象
由以上各個方法可知,writeToParcel方法負責將對象序列化,而CREATOR負責數據的反序列化,只要你的類實現了Parcelable接口,并實現以上方法,那么就能自動地對對象進行序列化和反序列化了。
注意:在writeToParcel方法中,調用了out.writeInt(data)方法,如果當前類有多個屬性,比如:int id,String name,String email,那么方法體可以寫為:
out.writeInt(id);
out.writeString(name);
out.writeString(email);
這樣寫后,在相應的MyParcelable(Parcel in)反序列化方法也必須如下寫:
in.readInt();
in.readString();
in.readString();
即順序應該一一對應,否則,取出來的數據將會出錯。
到目前為止,介紹了Serialable接口和Parcelable接口,這是IPC機制中比較基礎的概念,應熟練掌握。希望我的文章能對你們的學習起到幫助作用。