標籤:roi nal 資料庫 參數 ade 相對 很多 還原序列化 單例
該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡量按照先易後難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相關知識,另外也借鑒了其他的優質部落格,在此向各位大神表示感謝,膜拜!!!另外,本系列文章知識可能需要有一定Android開發基礎和項目經驗的同學才能更好理解,也就是說該系列文章面向的是Android中進階開發工程師。
前言
上一篇中我們比較詳盡的分析了ServiceManager。那麼本篇我們來講一下Android序列化的相關知識。為什麼跨度那麼大,因為“任性”?其實不是的,同志們還記得上兩篇出現的Parcel嗎,Parcel是一個容器,他可以包含資料或者是對象引用,並且能夠用於Binder的傳輸。同時支援序列化以及跨進程之後進行還原序列化,同時其提供了很多方法協助開發人員完成這些功能。從上面的描述可以看出Parcel是處理序間通訊的資料載體。我們常常需要持久化一些對象,除了資料庫等持久化方案之外,把對象轉換成位元組數組並通過流的方式儲存在本地也是一個不錯的方法,另外當我們需要通過Intent和Binder傳輸資料是就需要使用序列化後的資料。
Java中的Serializable
Serializable 是Java所提供的一個序列化介面,它是一個空介面,為對象提供標準的序列化和還原序列化操作。使用Serializable來實現序列化相當簡單,只需要在需要序列化的類實現Serializable介面並在其中聲明一個類似下面的標識即可自動實現預設的序列化過程。
public class Person extends PersonParent implements Serializable { private static final long serialVersionUID = 1L; //靜態域 public static int static_field; //transient域 public transient int transient_field; //一個普通的域 public String desc; public Person(String desc) { this.desc = desc; } static class PersonSerializableProxy implements Serializable{ private String desc; private PersonSerializableProxy(Person s) { this.desc = s.desc; } /** * 與writeReplace相同,ObjectInputStream會通過反射調用 readResolve()這個方法, * 決定是否替換還原序列化出來的對象。 * @return */ private Object readResolve() { return new Person(desc); } } /** * * 在序列化一個對象時,ObjectOutputStream會通過反射首先調用writeReplace這個方法, * 在這裡我們可以替換真正送去序列的對象, * 如果我們沒有重寫,那序列化的對象就是最開始的對象。 * @return */ private Object writeReplace() { //序列化Person的時候我們並沒有直接寫入Person對象,而是寫入了PersonSerializableProxy對象 return new PersonSerializableProxy(this); } /** * 這裡主要是為了防止攻擊,任何以Person聲明的對象位元組流都是流氓!! * 因為我在writeReplace中已經把序列化的執行個體指向了SerializableProxy * @param stream * @throws InvalidObjectException */ private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("proxy requied!"); } public static void main(String[] args) throws IOException, ClassNotFoundException { Person person = new Person("desc"); person.transient_field = 100; person.static_field = 10086; ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cache.txt")); outputStream.writeObject(person); outputStream.flush(); outputStream.close(); person.static_field = 10087; ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cache.txt")); Person deserialObj = (Person) objectInputStream.readObject(); System.out.println(deserialObj);}}class PersonParent{ private String name; //PersonParent類要麼繼承自Serializable,要麼需要提供一個無參構造器。 public PersonParent() { } public PersonParent(String name) { this.name = name; }}
不過在使用中也需要注意以下幾個問題:
- serialVersionUID用來標識當前序列化對象的類版本,建議每一個實現Serialization的類都指定該域。當然如果我們沒有指定,JVM會根據類的資訊自動產生一個UID。
- 被transient描述的域和類的靜態變數是不會被序列化的,序列化是針對類執行個體。
- 需要進行序列化的對象所有的域都必須實現Serializable介面,不然會直接報錯NotSerializableException。當然,有兩個例外:域為空白 或者域被transient描述是不會報錯的。
- 如果一個實現了Serializable類的對象繼承自另外一個類,那麼這個類要麼需要繼承自Serializable,要麼需要提供一個無參構造器。
- 還原序列化產生的對象並不是通過構造器建立的,那麼很多依賴於構造器保證的約束條件在對象還原序列化時都無法保證。比如一個設計成單例的類如果能夠被序列化就可以分分鐘複製出多個執行個體...
Android中的Parcelable
相對於Serializable而言,Parcelable的使用要複雜一些
public class Book implements Parcelable { private String name; public Book(String name) { this.name = name; } protected Book(Parcel in) { name = in.readString(); } //還原序列化功能由CREATOR完成,在CREATOR的內部標明的如何建立序列對象和數組 public static final Creator<Book> CREATOR = new Creator<Book>() { //從Parcel中還原序列化對象 @Override public Book createFromParcel(Parcel in) { //其內部調用Parcel的一系列readXXX方法實現還原序列化過程 return new Book(in); } //建立序列化數組 @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } //序列化過程: //重寫writeToParcel方法,我們要在這裡逐一對需要序列化的屬性用Parcel的一系列writeXXX方法寫入 @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); }}
從上述代碼注釋可以看出,寫一個實現Parcelable介面的類還是比較麻煩的,和Serailable相比,我們需要在writeToParcel中按序寫入各個域到流中,同樣,在createFromParcel中我們需要自己返回一個Book對象。
Parcelable在使用上也與Serializable稍有不同
public class TestActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test2); //擷取一個Parcel容器 Parcel parcel = Parcel.obtain(); //需要序列化的對象 Book book = new Book("c++"); //把對象寫入Parcel parcel.writeParcelable(book,0); //Parcel讀寫共用一個位置計數,這裡一定要重設一下當前的位置 parcel.setDataPosition(0); //讀取Parcel Book book1 = parcel.readParcelable(Book.class.getClassLoader()); Log.d("TestActivity",book1.toString()); }}
Parcelable的寫
我們來看一下writeParcelable方法
[Parcel.java]
public final void writeParcelable(Parcelable p, int parcelableFlags) { //判斷p是否為空白 if (p == null) { writeString(null); return; } //① 先寫入p的類名 writeParcelableCreator(p); //② 調用我們重寫的writeToParcel方法,按順序寫入域 p.writeToParcel(this, parcelableFlags);}public final void writeParcelableCreator(Parcelable p) { //① 先寫入p的類名 String name = p.getClass().getName(); writeString(name);}
Parcelable的讀
我們來看readParcelable方法
[Parcel.java]
public final <T extends Parcelable> T readParcelable(ClassLoader loader) { //① 調用readParcelableCreator //這時獲得就是我們自訂的CREATOR Parcelable.Creator<?> creator = readParcelableCreator(loader); if (creator == null) { return null; } // 判斷當前creator是不是Parcelable.ClassLoaderCreator<?>的執行個體 if (creator instanceof Parcelable.ClassLoaderCreator<?>) { //如果是的話,,我們調用reateFromParcel(this, loader); Parcelable.ClassLoaderCreator<?> classLoaderCreator = (Parcelable.ClassLoaderCreator<?>) creator; return (T) classLoaderCreator.createFromParcel(this, loader); } //調用我們自訂的CREATOR中重寫的createFromParcel方法 return (T) creator.createFromParcel(this);}public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) { //首先把類名讀取出來 String name = readString(); Parcelable.Creator<?> creator; //mCreators做了一下緩衝,如果之前某個classloader把一個parcelable的Creator擷取過 //那麼就不需要通過反射去尋找了 synchronized (mCreators) { HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader); if (map == null) { map = new HashMap<>(); mCreators.put(loader, map); } creator = map.get(name); if (creator == null) { try { ClassLoader parcelableClassLoader = (loader == null ? getClass().getClassLoader() : loader); //載入我們自己實現Parcelable介面的類 Class<?> parcelableClass = Class.forName(name, false, parcelableClassLoader); Field f = parcelableClass.getField("CREATOR"); Class<?> creatorType = f.getType(); creator = (Parcelable.Creator<?>) f.get(null); } catch (Exception e) { //catch exception } if (creator == null) { throw new BadParcelableException("Parcelable protocol requires a " + "non-null Parcelable.Creator object called " + "CREATOR on class " + name); } map.put(name, creator); } } return creator;}
我們的測試例子讀取Parcel
Book book1 = parcel.readParcelable(Book.class.getClassLoader());
可以看到我們在使用
readParcelable的時候,傳入的參數是Book類的類載入器,根據我們上面的代碼,我們知道我們先會通過反射擷取定義在Book類中的CREATOR屬性,我們回想一下在Book類中是怎麼定義CREATOR的
public static final Creator<Book> CREATOR = new Creator<Book>() { //從Parcel中還原序列化對象 @Override public Book createFromParcel(Parcel in) { //其內部調用Parcel的一系列readXXX方法實現還原序列化過程 return new Book(in); } //建立序列化數組 @Override public Book[] newArray(int size) { return new Book[size]; } };
我們得到CREATOR屬性後,調用它的createFromParcel方法,由多態可知調用的實際我們定義在CREATOR內的createFromParcel方法,在該方法內我們建立了Book對象(內部實現是通過Parcel的一系列readXXX方法)並返回。至此我們就得到了還原序列化的對象
本篇總結
我們本篇詳細分析了Android序列化相關知識,你可以使用Java中的Serializable也可以使用Parcelable。
下篇預告前面的文章中是對前面所講文章做一個小結。讀者敬請期待哦。
此致,敬禮
Android開發之漫漫長途 X——Android序列化