一、引言
數據的序列化在Android開發中占據着重要的地位,無論是在進程間通信、本地數據存儲又或者是網絡數據傳輸都離不開序列化的支持。而針對不同場景選擇合適的序列化方案對於應用的性能有着極大的影響。
從廣義上講,數據序列化就是將數據結構或者是對象轉換成我們可以存儲或者傳輸的數據格式的一個過程,在序列化的過程中,數據結構或者對象將其狀態信息寫入到臨時或者持久性的存儲區中,而在對應的反序列化過程中,則可以說是生成的數據被還原成數據結構或對象的過程。
這樣來說,數據序列化相當於是將我們原先的對象序列化概念做出了擴展,在對象序列化和反序列化中,我們熟知的有兩種方法,其一是Java語言中提供的Serializable接口,其二是Android提供的Parcelable接口。而在這里,因為我們對這個概念做出了擴展,因此也需要考慮幾種專門針對數據結構進行序列化的方法,如現在那些個開放API一般返回的數據都是JSON格式的,又或者是我們Android原生的SQLite數據庫來實現數據的本地存儲,從廣義上來說,這些都可以算做是數據的序列化。
二、Serializable接口
正如前面提到的,Serializable接口是Java語言的特性,是最簡單也是使用最廣泛的序列化方案之一,這邊需要注意的一點是Serializable接口是一個標識接口,無需實現方法,Java便會對這個對象進行序列化操作。
在這里實現了Serializable接口的對象才可以序列化,將Java對象轉換成字節序列,而對應的反序列化則是將字節序列恢復成Java對象的過程。
在需要序列化的類中會用到serialVersionUID去標識這個序列化對象,即僅當序列化后的數據中的SerialVersionUID與當前類的serialVersionUID相同時才能被正常的反序列化。
import java.io.*; public class User implements Serializable{ private static final long serialVersionUID= 123456; public int userId; public String userName; public boolean isMale; public User(int userId,String userName,boolean isMale){ this.userId=userId; this.userName=userName; this.isMale = isMale; } public boolean toSerial(User user) throws IOException{ ObjectOutputStream out=null; boolean status=false; try{ out = new ObjectOutputStream(new FileOutputStream("cache.txt")); out.writeObject(user); status=true; }catch(FileNotFoundException e){ System.out.println("NO FILE"); }finally{ if(out!=null) out.close(); } return status; } public User toObject(String filename) throws IOException{ ObjectInputStream in=null; boolean status=false; User user=null; try{ in = new ObjectInputStream(new FileInputStream(filename)); user=(User) in.readObject(); }catch(ClassNotFoundException e){ System.out.println("No file"); }finally{ if(in!=null) in.close(); } return user; } public static void main(String[] args) throws IOException{ User user = new User(0,"jake",true); System.out.println(user.toSerial(user)); System.out.println(user.toObject("cache.txt").getClass()); } }
此外,需要注意的:靜態成員變量是屬於類而不屬於對象的,所以顯然它不會參與到對象的序列化過程中。其次用transient關鍵字標記的成員變量不參與到序列化過程中。最后,這種序列化方式是基於磁盤或者網絡的。
三、Parcelable接口
Parcelable接口是Android API提供的接口,從某種程度上來說,它更適用於Android平台上。不同於Serializable,它是基於內存的,由於內存中讀寫速度高於磁盤,所以Parcelable接口被廣泛用於跨進程對象的傳遞。
下面貼上一個簡單的Parcelable接口的序列化過程:
import android.os.Parcel; import android.os.Parcelable; public class User implements Parcelable { public int userId; public String userName; public boolean isMale; public User(int userId,String userName,boolean isMale) { this.userId=userId; this.userName=userName; this.isMale=isMale; } public static final Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; public User(Parcel in) { userId=in.readInt(); userName = in.readString(); isMale=in.readInt()==1; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(userId); out.writeString(userName); out.writeInt(isMale?1:0); } }
從上面可以看出,實現一個Parcelable接口,需要實現以下幾個方法:
1.構造函數:從序列化后的對象中創建原始對象
2.describeContents :接口內容的描述,一般默認返回0即可
3.writeToParcel:序列化的方法,將類的數據寫到parcel容器中
4.靜態的parcelable.Creator接口,這個接口包含兩個方法
1)createFormParcel:反序列化的方法,將Parcel還原成Java對象
2)newArray:提供給外部類反序列化這個數組使用。
四、兩種對象序列化方法的對比
Serializable是Java中的序列化接口,其使用起來簡單但開銷較大(因為Serializable在序列化過程中使用了反射機制,故而會產生大量的臨時變量,從而導致頻繁的GC),並且在讀寫數據過程中,它是通過IO流的形式將數據寫入到硬盤或者傳輸到網絡上。
而Parcelable則是以IBinder作為信息載體,在內存上開銷比較小,因此在內存之間進行數據傳遞時,推薦使用Parcelable,而Parcelable對數據進行持久化或者網絡傳輸時操作復雜,一般這個時候推薦使用Serializable。
另外Serializable在使用時比較簡單,而Parcelable在使用時需要手動去實現接口中的方法,為了規避使用Parcelable接口時的麻煩,我們下面介紹一個插件,從而自動生成對應的代碼。
五、Parcelable插件
為了避免寫大量的模板代碼,這邊介紹一個在Android Strudio中的插件,Android Parcelable code generator。在Pulgins中下載並按照該插件,接下來當我們需要用到Parcelable接口時,該插件就能自動幫我們將類對象轉換成實現Parcelable接口的形式。
具體示例如下,
/** * Created by DB on 2017/6/24. */ public class BookItem { public String mName; public long mLastTime; public String mTitle; public String mPath; }
然后類似與生成getter和setter代碼那樣,我們就可以直接自動生成Parcelable形式的代碼,結果如下所示:
import android.os.Parcel; import android.os.Parcelable; /** * Created by DB on 2017/6/24. */ public class BookItem implements Parcelable { public String mName; public long mLastTime; public String mTitle; public String mPath; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.mName); dest.writeLong(this.mLastTime); dest.writeString(this.mTitle); dest.writeString(this.mPath); } public BookItem() { } protected BookItem(Parcel in) { this.mName = in.readString(); this.mLastTime = in.readLong(); this.mTitle = in.readString(); this.mPath = in.readString(); } public static final Parcelable.Creator<BookItem> CREATOR = new Parcelable.Creator<BookItem>() { @Override public BookItem createFromParcel(Parcel source) { return new BookItem(source); } @Override public BookItem[] newArray(int size) { return new BookItem[size]; } }; }
有了這個插件,使用Parcelable接口顯然方便了許多(可以偷好多懶)
六、數據的序列化方案
下面講到的是廣義上的序列化方案,不同於前面兩種狹義或者說是對象序列化方案,接下來的幾種方案針對於數據的傳輸和存儲過程中的序列化方案
1.SQLite
SQLite主要用於存儲復雜的關系型數據,Android支持原生支持SQLite數據庫相關操作(SQLiteOpenHelper),不過由於原生API接口並不友好,所以產生了不少封裝了SQLite的ORM框架。
2.SharedPreferences
SharedPreferences是Android平台上提供的一個輕量級存儲API,一般用於存儲常用的配置信息,其本質是一個鍵值對存儲,支持常用的數據類型如boolean、float、int、long以及String的存儲和讀取。
使用SharedPreferences讀取和存儲操作如下:
讀取:
1) 獲取Sharedpreferences對象
SharedPreferences mPreferences = context.getCSharedPreferences(PREFERENCES_NAME,Context.MODE_PRIVATE);
2.通過SharedPReferences對象讀取存儲在SharedPreferences中的數據
mPreferences.getBoolean(key,defValue);
存儲:
1)獲取SharedPreferences.Editor對象
SharedPreferences.Editor editor = mPreferences.edit();
2)通過SharedPreferences.Editor對象寫入數據到SharedPreferences中。
mEditor.putBoolean(key,b);
3)調用commit函數將寫入的數據提交,從而完成數據存儲操作。
mEditor.commit();
3.JSON
JSON是一種輕量級的數據交互格式,由於其相對於XML,體積更小,在網絡上傳輸時更加介紹瀏覽,被廣泛用於移動端。大部分APP與服務端的通信都是使用JSON格式進行交互。