今天再來談談Android中的對象序列化,你瞭解多少呢?java
序列化指的是講對象變成有序的字節流
,變成字節流以後才能進行傳輸存儲等一系列操做。
反序列化就是序列化的相反操做
,也就是把序列化生成的字節流轉爲咱們內存的對象。android
是Java
提供的一個序列化接口,是一個空接口,專門爲對象提供序列化和反序列化操做。具體使用以下:數組
public class User implements Serializable { private static final long serialVersionUID=519067123721561165l; private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } }
實現Serializable
接口,聲明一個serialVersionUID
。緩存
到這裏可能有人就問了,不對啊,平時沒有這個serialVersionUID
啊。沒錯,serialVersionUID
不是必須的,由於不寫的話,系統會自動生成這個變量。它有什麼用呢?當序列化的時候,系統會把當前類的serialVersionUID
寫入序列化的文件中,當反序列化的時候會去檢測這個serialVersionUID
,看他是否和當前類的serialVersionUID
一致,同樣則能夠正常反序列化,若是不同就會報錯了。網絡
因此這個serialVersionUID
就是序列化和反序列化過程當中的一個標識,表明一致性。不加的話會有什麼影響?若是咱們序列化後,改動了這個類的某些成員變量,那麼serialVersionUID
就會改變,這時候再拿以前序列化的數據來反序列化就會報錯。因此若是咱們手動指定serialVersionUID
就能保證最大限度來恢復數據。ide
Serializable
的實質實際上是是把Java對象序列化爲二進制文件,而後就能在進程之間傳遞,而且用於網絡傳輸或者本地存儲等一系列操做,由於他的本質就存儲了文件。能夠看看源碼:函數
private void writeObject0(Object obj, boolean unshared) throws IOException { ... try { Object orig = obj; Class<?> cl = obj.getClass(); ObjectStreamClass desc; desc = ObjectStreamClass.lookup(cl, true); if (obj instanceof Class) { writeClass((Class) obj, unshared); } else if (obj instanceof ObjectStreamClass) { writeClassDesc((ObjectStreamClass) obj, unshared); // END Android-changed: Make Class and ObjectStreamClass replaceable. } else if (obj instanceof String) { writeString((String) obj, unshared); } else if (cl.isArray()) { writeArray(obj, desc, unshared); } else if (obj instanceof Enum) { writeEnum((Enum<?>) obj, desc, unshared); } else if (obj instanceof Serializable) { writeOrdinaryObject(obj, desc, unshared); } else { if (extendedDebugInfo) { throw new NotSerializableException( cl.getName() + "\n" + debugInfoStack.toString()); } else { throw new NotSerializableException(cl.getName()); } } } ... } private void writeOrdinaryObject(Object obj, ObjectStreamClass desc, boolean unshared) throws IOException { ... try { desc.checkSerialize(); //寫入二進制文件,普通對象開頭的魔數0x73 bout.writeByte(TC_OBJECT); //寫入對應的類的描述符,見底下源碼 writeClassDesc(desc, false); handles.assign(unshared ? null : obj); if (desc.isExternalizable() && !desc.isProxy()) { writeExternalData((Externalizable) obj); } else { writeSerialData(obj, desc); } } finally { if (extendedDebugInfo) { debugInfoStack.pop(); } } } public long getSerialVersionUID() { // 若是沒有定義serialVersionUID,序列化機制就會調用一個函數根據類內部的屬性等計算出一個hash值 if (suid == null) { suid = AccessController.doPrivileged( new PrivilegedAction<Long>() { public Long run() { return computeDefaultSUID(cl); } } ); } return suid.longValue(); }
能夠看到是經過反射獲取對象以及對象屬性的相關信息,而後將數據寫到了一個二進制文件
,而且寫入了序列化協議版本等等。
而獲取·serialVersionUID·的邏輯也體現出來,若是id爲空則會生成計算一個hash值。post
Android自帶的接口,使用起來要麻煩不少:須要實現Parcelable接口,重寫describeContents(),writeToParcel(Parcel dest, @WriteFlags int flags)
,並添加一個靜態成員變量CREATOR
並實現Parcelable.Creator
接口學習
public class User implements Parcelable { private int id; protected User(Parcel in) { id = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); } @Override public int describeContents() { return 0; } 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 int getId() { return id; } public void setId(int id) { this.id = id; } }
createFromParcel
,User(Parcel in) ,表明從序列化的對象中建立原始對象newArray
,表明建立指定長度的原始對象數組writeToParcel
,表明將當前對象寫入到序列化結構中。describeContents
,表明返回當前對象的內容描述。若是還有文件描述符,返回1,不然返回0。Parcelable
的存儲是經過Parcel
存儲到內存的,簡單地說,Parcel提供了一套機制,能夠將序列化以後的數據寫入到一個共享內存中,其餘進程經過Parcel
能夠從這塊共享內存中讀出字節流,並反序列化成對象。ui
這其中實際又是經過native
方法實現的。具體邏輯我就沒有去分析了,若是有大神朋友能夠在評論區解析下。
固然,Parcelable
也是能夠持久化的,涉及到Parcel中的unmarshall
和marshall
方法。 這裏簡單貼一下代碼:
protected void saveParce() { FileOutputStream fos; try { fos = getApplicationContext().openFileOutput(TAG, Context.MODE_PRIVATE); BufferedOutputStream bos = new BufferedOutputStream(fos); Parcel parcel = Parcel.obtain(); parcel.writeParcelable(new ParceData(), 0); bos.write(parcel.marshall()); bos.flush(); bos.close(); fos.flush(); fos.close(); } catch (Exception e) { e.printStackTrace(); } } protected void loadParce() { FileInputStream fis; try { fis = getApplicationContext().openFileInput(TAG); byte[] bytes = new byte[fis.available()]; fis.read(bytes); Parcel parcel = Parcel.obtain(); parcel.unmarshall(bytes, 0, bytes.length); parcel.setDataPosition(0); ParceData data = parcel.readParcelable(ParceData.class.getClassLoader()); fis.close(); } catch (Exception e) { e.printStackTrace(); } }
0)二者的區別,咱們該怎麼選擇?
Serializable
是Java提供的序列化接口,使用簡單可是開銷很大,序列化和反序列化過程都須要大量I/O
操做。
Parcelable
是Android中提供的,也是Android
中推薦的序列化方式。雖然使用麻煩,可是效率很高。
因此,若是是內存序列化層面,那麼仍是建議Parcelable
,由於他效率會比較高。
若是是網絡傳輸和存儲磁盤狀況,就推薦Serializable
,由於序列化方式比較簡單,並且Parcelable不能保證,當外部條件發生變化時數據的連續性。
1)對於內存序列化方面建議用Parcelable
,爲何呢?
Serializable
是存儲了一個二進制文件,因此會有頻繁的IO操做,消耗也比較大,並且用到了大量反射,反射操做也是耗時的。相比之下Parcelable
就要效率高不少。2)對於數據持久化仍是建議用Serializable
,爲何呢?
Serializable
自己就是存儲到二進制文件,因此用於持久化比較方便。而Parcelable
序列化是在內存中操做,若是進程關閉或者重啓的時候,內存中的數據就會消失,那麼Parcelable
序列化用來持久化就有可能會失敗,也就是數據不會連續完整。並且Parcelable
還有一個問題是兼容性,每一個Android版本可能內部實現都不同,知識用於內存中也就是傳遞數據的話是不影響的,可是若是持久化可能就會有問題了,低版本的數據拿到高版本可能會出現兼容性問題。 因此仍是建議用Serializable
進行持久化。3)Parcelable必定比Serializable快嗎?
Serializable
實現了writeObject()
以及readObject()
,在平均每檯安卓設備上,Serializable
序列化速度大於Parcelable
3.6倍,反序列化速度大於1.6倍.具體緣由就是由於Serilazable
的實現方式中,是有緩存的概念的,當一個對象被解析事後,將會緩存在HandleTable
中,當下一次解析到同一種類型的對象後,即可以向二進制流中,寫入對應的緩存索引便可。可是對於Parcel
來講,沒有這種概念,每一次的序列化都是獨立的,每個對象,都看成一種新的對象以及新的類型的方式來處理。
有一塊兒學習的小夥伴能夠關注下❤️個人公衆號——碼上積木,天天剖析一個知識點,咱們一塊兒積累知識。