序列化
將數(shù)據(jù)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成二進(jìn)制串的過程。
反序列化
將在序列化過程中所生成的二進(jìn)制串轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)或者對象的過程
序列化/反序列化的目的
簡單的概括
- 序列化:主要用于網(wǎng)絡(luò)傳輸,數(shù)據(jù)持久化,一般序列化也稱為編碼(Encode)
- 反序列化:主要用于從網(wǎng)絡(luò),磁盤上讀取字節(jié)數(shù)組還原成原始對象,一般反序列化也稱為解碼 (Decode)
具體的講:
- 永久的保存對象數(shù)據(jù)(將對象數(shù)據(jù)保存在文件當(dāng)中,或者是磁盤中);
- 通過序列化操作將對象數(shù)據(jù)在網(wǎng)絡(luò)上進(jìn)行傳輸(由于網(wǎng)絡(luò)傳輸是以字節(jié)流的方式對數(shù)據(jù)進(jìn)行傳輸?shù)模虼诵蛄谢哪康氖菍ο髷?shù)據(jù)轉(zhuǎn)換成字節(jié)流的形式) ;
- 將對象數(shù)據(jù)在進(jìn)程之間進(jìn)行傳遞(Activity之間傳遞對象數(shù)據(jù)時,需要在當(dāng)前的Activity中對對象數(shù)據(jù)進(jìn)行序列化操作。在另一個Activity中需要進(jìn)行反序列化操作講數(shù)據(jù)取出);
- Java平臺允許我們在內(nèi)存中創(chuàng)建可復(fù)用的Java對象,但一般情況下,只有當(dāng)JVM處于運行時,這些對象才可能存在,即這些對象的生命周期不會比JVM的生命周期更長(即每個對象都在JVM中) 。但在現(xiàn)實應(yīng)用中,就可能要停止JVM運行,但有要保存某些指定的對象,并在將來重新讀取被保存的對象。這是Java對象序列化就能夠?qū)崿F(xiàn)該功能。(可選擇入數(shù)據(jù)庫、或文件的形式保存);
- 序列化對象的時候只是針對變量進(jìn)行序列化,不針對方法進(jìn)行序列化;
- 在Intent之間,基本的數(shù)據(jù)類型直接進(jìn)行相關(guān)傳遞即可,但是一旦數(shù)據(jù)類型比較復(fù)雜的時候,就需要進(jìn) 行序列化操作了。
常見的序列化和反序列化協(xié)議
XML&SOAP
XML 是一種常用的序列化和反序列化協(xié)議,具有跨機(jī)器,跨語言等優(yōu)點,SOAP(Simple Object Access protocol) 是一種被廣泛應(yīng)用的,基于 XML 為序列化和反序列化協(xié)議的結(jié)構(gòu)化消息傳遞協(xié)議。
JSON(Javascript Object Notation)
JSON 起源于弱類型語言 Javascript, 它的產(chǎn)生來自于一種稱之為"Associative array"的概念,其本質(zhì)是 就是采用"Attribute-value"的方式來描述對象。實際上在 Javascript 和 PHP 等弱類型語言中,類的描 述方式就是 Associative array。JSON 的如下優(yōu)點,使得它快速成為最廣泛使用的序列化協(xié)議之一。
這種 Associative array 格式非常符合工程師對對象的理解;
它保持了 XML 的人眼可讀(Human-readable)的優(yōu)點;
相對于 XML 而言,序列化后的數(shù)據(jù)更加簡潔。來自于的以下鏈接的研究表明:XML 所產(chǎn)生序列化之后文件的大小接近 JSON 的兩倍;
它具備 Javascript 的先天性支持,所以被廣泛應(yīng)用于 Web browser 的應(yīng)用常景中,是 Ajax 的事實標(biāo)準(zhǔn)協(xié)議;
與 XML 相比,其協(xié)議比較簡單,解析速度比較快;
松散的 Associative array 使得其具有良好的可擴(kuò)展性和兼容性。
Protobuf
Protobuf 具備了優(yōu)秀的序列化協(xié)議的所需的眾多典型特征。
- 標(biāo)準(zhǔn)的 IDL 和 IDL 編譯器,這使得其對工程師非常友好。
- 序列化數(shù)據(jù)非常簡潔,緊湊,與 XML 相比,其序列化之后的數(shù)據(jù)量約為 1/3 到 1/10。
- 解析速度非常快,比對應(yīng)的 XML 快約 20-100 倍。
- 提供了非常友好的動態(tài)庫,使用非常簡介,反序列化只需要一行代碼。
Serializable接口
是 Java 提供的序列化接口,它是一個空接口:
public interface Serializable {
}
Serializable 用來標(biāo)識當(dāng)前類可以被 ObjectOutputStream 序列化,以及被 ObjectInputStream 反序列 化。
Serializable入門
public class Student implements Serializable {
private static final long serialVersionUID = -2100492893943893602L;//serialVersionUID唯一標(biāo)識了一個可序列化的類
private String name;
private String sax;
private Integer age;
private List<Course> courses;//Course也需要實現(xiàn)Serializable接口
//用transient關(guān)鍵字標(biāo)記的成員變量不參與序列化
//(在被反序列化后,transient變量的值被設(shè)為初始值,如int型的是 0,對象型的是null)
private transient Date createTime;
//靜態(tài)成員變量屬于類不屬于對象,所以不會參與序列化
//(對象序列化保存的是對象的“狀態(tài)”,也 就是它的成員變量,因此序列化不會關(guān)注靜態(tài)變量)
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
public Student() {
System.out.println("Student: empty");
}
public Student(String name, String sax, Integer age) {
System.out.println("Student: " + name + " " + sax + " " + age);
this.name = name;
this.sax = sax;
this.age = age;
courses = new ArrayList<>();
createTime = new Date();、
}
}
//Course也需要實現(xiàn)Serializable接口
public class Course implements Serializable {
private static final long serialVersionUID = 667279791530738499L;
private String name;
private float score;
}
Serializable 有以下幾個特點:
- 可序列化類中,未實現(xiàn) Serializable 的屬性狀態(tài)無法被序列化/反序列化;
- 也就是說,反序列化一個類的過程中,它的非可序列化的屬性將會調(diào)用無參構(gòu)造函數(shù)重新創(chuàng)建 ;
- 因此這個屬性的無參構(gòu)造函數(shù)必須可以訪問,否者運行時會報錯;
- 一個實現(xiàn)序列化的類,它的子類也是可序列化的。
serialVersionUID與兼容性
-
serialVersionUID的作用
serialVersionUID 用來表明類的不同版本間的兼容性。如果你修改了此類, 要修改此值。否則以前用老版本的類序列化的類恢復(fù)時會報錯: InvalidClassException
-
設(shè)置方式
在JDK中,可以利用JDK的bin目錄下的serialver.exe工具產(chǎn)生這個serialVersionUID,對于 Test.class,執(zhí)行命令:serialver Test
-
兼容性問題
為了在反序列化時,確保類版本的兼容性,最好在每個要序列化的類中加入 private static final long serialVersionUID這個屬性,具體數(shù)值自己定義。
這樣,即使某個類在與之對應(yīng)的對象已經(jīng)序列化出去后做了修改,該對象依然可以被正確反序列化。否則,如果不顯式定義該屬性,這個屬性值將由JVM根據(jù)類的相關(guān)信息計算,而修改后的類的計算結(jié)果與修改前的類的計算結(jié)果往往不同,從而造成對象的反序列化因為類版本不兼容而失敗。
不顯式定義這個屬性值的另一個壞處是,不利于程序在不同的JVM之間的移植。因為不同的編譯器 實現(xiàn)該屬性值的計算策略可能不同,從而造成雖然類沒有改變,但是因為JVM不同,出現(xiàn)因類版本不兼容而無法正確反序列化的現(xiàn)象出現(xiàn)。
因此 JVM 規(guī)范強烈建議我們手動聲明一個版本號,這個數(shù)字可以是隨機(jī)的,只要固定不變就可以。同時最好是 private 和 final 的,盡量保證不變。
Externalizable接口
public interface Externalizable extends java.io.Serializable {
void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
簡單使用
public class Course1 implements Externalizable {
private static final long serialVersionUID = 667279791530738499L;
private String name;
private float score;
...
@Override
public void writeExternal(ObjectOutput objectOutput) throws IOException{
System.out.println("writeExternal");
objectOutput.writeObject(name);
objectOutput.writeFloat(score);
}
@Override
public void readExternal(ObjectInput objectInput) throws IOException,
ClassNotFoundException {
System.out.println("readExternal");
name = (String)objectInput.readObject();
score = objectInput.readFloat();
}
...
public static void main(String... args) throws Exception {
Course1 course = new Course1("英語", 12f);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(course);
course.setScore(78f);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bs));
Course1 course1 = (Course1) ois.readObject();
System.out.println("course1: " + course1);
}
readObject/writeObject原理分析
以 oos.writeObject(obj) 為例分析
-
ObjectOutputStream的構(gòu)造函數(shù)設(shè)置enableOverride = false
public ObjectOutputStream(OutputStream var1) throws IOException { this.verifySubclass(); this.bout = new ObjectOutputStream.BlockDataOutputStream(var1); this.handles = new ObjectOutputStream.HandleTable(10, 3.0F); this.subs = new ObjectOutputStream.ReplaceTable(10, 3.0F); this.enableOverride = false; //有參構(gòu)造函數(shù),設(shè)置enableOverride = false this.writeStreamHeader(); this.bout.setBlockDataMode(true); if (extendedDebugInfo) { this.debugInfoStack = new ObjectOutputStream.DebugTraceInfoStack(); } else { this.debugInfoStack = null; } }
-
所以writeObject方法執(zhí)行的是writeObject0(obj, false);
public final void writeObject(Object var1) throws IOException { if (this.enableOverride) { this.writeObjectOverride(var1); } else { try { this.writeObject0(var1, false); //enableOverride=false,走這里 } catch (IOException var3) { if (this.depth == 0) { this.writeFatalException(var3); } throw var3; } } }
-
在writeObject0方法中,代碼非常多,看重點
private void writeObject0(Object var1, boolean var2) throws IOException { ... if (var1 instanceof Enum) { this.writeEnum((Enum)var1, var7, var2); } else { if (!(var1 instanceof Serializable)) { if (extendedDebugInfo) { throw new NotSerializableException(var6.getName() + "\n" + this.debugInfoStack.toString()); } //如果沒有實現(xiàn)Serializable接口,會報NotSerializableException throw new NotSerializableException(var6.getName()); } //實現(xiàn)Serializable接口,走這里 this.writeOrdinaryObject(var1, var7, var2);//實現(xiàn)了Serializable } return; ... }
-
在writeOrdinaryObject(obj, desc, unshared)方法中
private void writeOrdinaryObject(Object var1, ObjectStreamClass var2, boolean var3) throws IOException { if (extendedDebugInfo) { this.debugInfoStack.push((this.depth == 1 ? "root " : "") + "object (class \"" + var1.getClass().getName() + "\", " + var1.toString() + ")"); } try { var2.checkSerialize(); this.bout.writeByte(115); this.writeClassDesc(var2, false); this.handles.assign(var3 ? null : var1); if (var2.isExternalizable() && !var2.isProxy()) { //如果對象實現(xiàn)了Externalizable接口,那么執(zhí)行 writeExternalData((Externalizable) obj)方法 this.writeExternalData((Externalizable)var1); } else { //如果對象實現(xiàn)的是Serializable接口,那么執(zhí)行的是writeSerialData(obj, desc) this.writeSerialData(var1, var2); } } finally { if (extendedDebugInfo) { this.debugInfoStack.pop(); } } }
-
如果對象實現(xiàn)的是Serializable接口,走的是writeSerialData(var1, var2)方法,最終寫序列化的地方
private void writeSerialData(Object var1, ObjectStreamClass var2) throws IOException { ClassDataSlot[] var3 = var2.getClassDataLayout(); for(int var4 = 0; var4 < var3.length; ++var4) { ObjectStreamClass var5 = var3[var4].desc; //如果writeObjectMethod != null(目標(biāo)類中定義了私有的writeObject方法),那么將調(diào)用目標(biāo)類中的writeObject方法 if (var5.hasWriteObjectMethod()) { ObjectOutputStream.PutFieldImpl var6 = this.curPut; this.curPut = null; SerialCallbackContext var7 = this.curContext; if (extendedDebugInfo) { this.debugInfoStack.push("custom writeObject data (class \"" + var5.getName() + "\")"); } try { this.curContext = new SerialCallbackContext(var1, var5); this.bout.setBlockDataMode(true); var5.invokeWriteObject(var1, this);//定義了writeObject方法,去執(zhí)行invokeWriteObject(var1, this) this.bout.setBlockDataMode(false); this.bout.writeByte(120); } finally { this.curContext.setUsed(); this.curContext = var7; if (extendedDebugInfo) { this.debugInfoStack.pop(); } } this.curPut = var6; } else { //如果如果writeObjectMethod == null, 那么將調(diào)用默認(rèn)的 defaultWriteFields方法來讀取目標(biāo)類中的屬性 this.defaultWriteFields(var1, var5); } } }
-
調(diào)用了ObjectStreamClass的hasWriteObjectMethod()來判斷是否定義了私有的writeObject方法。
boolean hasWriteObjectMethod() { this.requireInitialized(); return this.writeObjectMethod != null; }
-
那writeObjectMethod變量是在哪里定義的呢?在ObjectStreamClass構(gòu)造函數(shù)中。
private ObjectStreamClass(final Class<?> var1) { ... if (ObjectStreamClass.this.externalizable) { ObjectStreamClass.this.cons = ObjectStreamClass.getExternalizableConstructor(var1); } else { ObjectStreamClass.this.cons = ObjectStreamClass.getSerializableConstructor(var1); //在序列化(反序列化)的時候, ObjectOutputStream(ObjectInputStream) //會尋找目標(biāo)類中的私有的writeObject(readObject)方法, //賦值給變量writeObjectMethod(readObjectMethod) ObjectStreamClass.this.writeObjectMethod = ObjectStreamClass.getPrivateMethod(var1, "writeObject", new Class[]{ObjectOutputStream.class}, Void.TYPE); ObjectStreamClass.this.readObjectMethod = ObjectStreamClass.getPrivateMethod(var1, "readObject", new Class[]{ObjectInputStream.class}, Void.TYPE); ObjectStreamClass.this.readObjectNoDataMethod = ObjectStreamClass.getPrivateMethod(var1, "readObjectNoData", (Class[])null, Void.TYPE); ObjectStreamClass.this.hasWriteObjectData = ObjectStreamClass.this.writeObjectMethod != null; } ... }
Serializable需要注意的坑
-
多引用寫入
在默認(rèn)情況下, 對于一個實例的多個引用,為了節(jié)省空間,只會寫入一次,后面會追加幾個字節(jié)代表某 個實例的引用。
-
子類實現(xiàn)序列化,父類不實現(xiàn)序列化/ 對象引用
在readObject時拋出java.io.NotSerializableException異常。
-
類的演化
反序列化目標(biāo)類多一個字段,并不會報錯,只是實賦成了默認(rèn)值。
還有,如果寫入的多一個字段,讀出的少一個字段,也是不會報錯的。 -
枚舉類型
事實上序列化Enum對象時,并不會保存元素的值,只會保存元素的name。這樣,在不依賴元素值的 前提下,ENUM對象如何更改都會保持兼容性。
Parcelable接口
Parcelable是Android為我們提供的序列化的接口,Parcelable相對于Serializable的使用相對復(fù)雜一些,但 Parcelable的效率相對Serializable也高很多,這一直是Google工程師引以為傲的,有時間的可以看一下 Parcelable和Serializable的效率對比,Parcelable vs Serializable 號稱快10倍的效率。
Parcelable是Android SDK提供的,它是基于內(nèi)存的,由于內(nèi)存讀寫速度高于硬盤,因此Android中的跨進(jìn)程對象的傳遞一般使用Parcelable。
Parcelable與Serializable的性能比較
Serializable性能分析
Serializable是Java中的序列化接口,其使用起來簡單但開銷較大(因為Serializable在序列化過程中使 用了反射機(jī)制,故而會產(chǎn)生大量的臨時變量,從而導(dǎo)致頻繁的GC),并且在讀寫數(shù)據(jù)過程中,它是通 過IO流的形式將數(shù)據(jù)寫入到硬盤或者傳輸?shù)骄W(wǎng)絡(luò)上。
Parcelable性能分析
Parcelable則是以IBinder作為信息載體,在內(nèi)存上開銷比較小,因此在內(nèi)存之間進(jìn)行數(shù)據(jù)傳遞時,推薦使用Parcelable,而Parcelable對數(shù)據(jù)進(jìn)行持久化或者網(wǎng)絡(luò)傳輸時操作復(fù)雜,一般這個時候推薦使用 Serializable。
注意點:Parcelable安全漏洞
性能比較總結(jié)描述
Parcelable的性能要強于Serializable的原因:
在內(nèi)存的使用中,前者在性能方面要強于后者;
后者在序列化操作的時候會產(chǎn)生大量的臨時變量(原因是使用了反射機(jī)制),從而導(dǎo)致GC的頻繁調(diào)用,因此在性能上會稍微遜色;
Parcelable是以Ibinder作為信息載體的。在內(nèi)存上的開銷比較小,因此在內(nèi)存之間進(jìn)行數(shù)據(jù)傳遞的時候,Android推薦使用Parcelable。既然是內(nèi)存方面比價有優(yōu)勢,那么自然就要優(yōu)先選擇;
-
在讀寫數(shù)據(jù)的時候,Parcelable是在內(nèi)存中直接進(jìn)行讀寫,而Serializable是通過使用IO流的形式將數(shù)據(jù)讀寫入在硬盤上;
但是雖然Parcelable的性能要強于Serializable,但是仍然有特殊的情況需要使用Serializable,而不去使用Parcelable。因為Parcelable無法將數(shù)據(jù)進(jìn)行持久化,因此在將數(shù)據(jù)保存在磁盤的時候,仍然需要使用后者,因為前者無法很好的將數(shù)據(jù)進(jìn)行持久化(原因是在不同的Android版本當(dāng)中,Parcelable可能會不同,因此數(shù)據(jù)的持久化方面仍然是使用Serializable)。
性能測試方法分析
-
通過將一個對象放到一個bundle里面,然后調(diào)用Bundle#writeToParcel(Parcel, int)方法來模擬;
傳遞對象給一個activity的過程,然后再把這個對象取出來。
在一個循環(huán)里面運行1000 次。
兩種方法分別運行10次來減少內(nèi)存整理,cpu被其他應(yīng)用占用等情況的干擾。
參與測試的對象就是上面的相關(guān)代碼
在多種Android軟硬件環(huán)境上進(jìn)行測試
兩種如何選擇
- 在使用內(nèi)存方面,Parcelable比Serializable性能高,所以推薦使用Parcelable;
- Serializable在序列化的時候會產(chǎn)生大量的臨時變量,從而引起頻繁的GC;
- Parcelable不能使用在要將數(shù)據(jù)存儲在磁盤上的情況,因為Parcelable不能很好的保證數(shù)據(jù)的持續(xù)性,在外界有變化的情況下,建議使用Serializable。
Parcelable與Serializable的區(qū)別
Parcelable | Serializable |
---|---|
直接在內(nèi)存操作,效率高,性能好 | 通過IO對硬盤操作,速度較慢 |
一般不能超過1M,修改內(nèi)核也只能4M | 大小不受限制 |
大量使用反射,產(chǎn)生內(nèi)存碎片 |
SQLite與SharedPreferences
SQLite主要用于存儲復(fù)雜的關(guān)系型數(shù)據(jù),Android支持原生支持SQLite數(shù)據(jù)庫相關(guān)操作 (SQLiteOpenHelper),不過由于原生API接口并不友好,所以產(chǎn)生了不少封裝了SQLite的ORM框架。
SharedPreferences是Android平臺上提供的一個輕量級存儲API,一般用于存儲常用的配置信息,其本質(zhì)是一個鍵值對存儲,支持常用的數(shù)據(jù)類型如boolean、float、int、long以及String的存儲和讀取。
相關(guān)面試題
-
①什么是 serialVersionUID?作用是什么?如果你不定義這個, 會發(fā)生什么?
②假設(shè)你有一個類,它序列化并存儲在持久性中,然后修改了該類以添加新字段。如果對已序列化的對象進(jìn)行反序列化,會發(fā)生什么情況?
serialVersionUID 是一個 private static final long 型 ID,當(dāng)它被印在對象上時,它通常是對象的哈希碼,你可以使用 serialver 這個 JDK 工具來查看序列化對象的 serialVersionUID。 SerialVerionUID 用于對象的版本控制。也可以在類文件中指定 serialVersionUID。不指定 serialVersionUID的后果是,當(dāng)你添加或修改類中的任何字段時,,則已序列化類將無法恢復(fù),因為為新類和舊序列化對象生成的 serialVersionUID 將有所不同。Java 序列化過程依賴于正確的序列化對象恢復(fù)狀態(tài)的,并在序列化對象序列版本不匹配的情況下引發(fā)java.io.InvalidClassException 無效類異常。
-
序列化時,你希望某些成員不要序列化?你如何實現(xiàn)它?
有時候也會變著形式問,比如問什么是瞬態(tài) trasient 變量,瞬態(tài)和靜態(tài)變量會不會得到序列化等,所以,如果你不希望任何字段是對象的狀態(tài)的一部分,然后聲明它靜態(tài)或瞬態(tài)根據(jù)你的需要,這樣就不會是在 Java 序列化過程中被包含在內(nèi)。
-
如果類中的一個成員未實現(xiàn)可序列化接口,會發(fā)生什么情況?
如果嘗試序列化實現(xiàn)可序列化的類的對象,但該對象包含對不可序列化類的引用,則在運行時將引發(fā)不可序列化異常 NotSerializableException
-
如果類是可序列化的,但其超類不是,則反序列化后從超級類繼承的實例變量的狀態(tài)如何?
Java 序列化過程僅在對象層次都是可序列化結(jié)構(gòu)中繼續(xù),即實現(xiàn) Java 中的可序列化接口,并且從超級類繼承的實例變量的值將通過調(diào)用構(gòu)造函數(shù)初始化,在反序列化過程中不可序列化的超級類。
-
①是否可以自定義序列化過程,或者是否可以覆蓋 Java 中的默認(rèn)序列化過程?
②假設(shè)新類的超級類實現(xiàn)可序列化接口,如何避免新類被序列化?
對于序列化一個對象需調(diào)用 ObjectOutputStream.writeObject(saveThisObject),并用 ObjectInputStream.readObject() 讀取對象,但 Java 虛擬機(jī)為你提供的還有一件事,是定義這兩個方法。如果在類中定義這兩種方法,則 JVM 將調(diào)用這兩種方法,而不是應(yīng)用默認(rèn)序列化機(jī)制。 你可以在此處通過執(zhí)行任何類型的預(yù)處理或后處理任務(wù)來自定義對象序列化和反序列化的行為。
-
在 Java 中的序列化和反序列化過程中使用哪些方法?
考察你是否熟悉 readObject() 的用法、writeObject()、readExternal() 和 writeExternal()。 Java 序列化由java.io.ObjectOutputStream類完成。該類是一個篩選器流,它封裝在較低級別的字節(jié)流中,以處理序列化機(jī)制。要通過序列化機(jī)制存儲任何對象,我們調(diào)用 ObjectOutputStream.writeObject(savethisobject),并反序列化該對象,我們稱之為 ObjectInputStream.readObject()方法。調(diào)用以 writeObject() 方法在 java 中觸發(fā)序列化過程。 關(guān)于 readObject() 方法,需要注意的一點很重要一點是,它用于從持久性讀取字節(jié),并從這些字節(jié)創(chuàng)建對象,并返回一個對象,該對象需要類型強制轉(zhuǎn)換為正確的類型。
-
反序列化后的對象,需要調(diào)用構(gòu)造函數(shù)重新構(gòu)造嗎
不會,因為是從二進(jìn)制直接解析出來的。適用的是 Object 進(jìn)行接收再強轉(zhuǎn),因此不是原來的那個對象。
-
序列化與持久化的關(guān)系和區(qū)別是什么?
序列化是為了進(jìn)程間數(shù)據(jù)交互而設(shè)計的,持久化是為了把數(shù)據(jù)存儲下來而設(shè)計的。
-
序列前的對象與序列化后的對象是什么關(guān)系?是("=="還是equal?是淺復(fù)制還是 深復(fù)制?)//枚舉
是一個深拷貝, 前后對象的引用地址不同
-
Android里面為什么要設(shè)計出Bundle而不是直接用Map結(jié)構(gòu)?
Bundle內(nèi)部是由ArrayMap實現(xiàn)的,ArrayMap的內(nèi)部實現(xiàn)是兩個數(shù)組,一個int數(shù)組是存儲對象數(shù)據(jù)對應(yīng)下標(biāo),一個對象數(shù)組保存key和value,內(nèi)部使用二分法對key進(jìn)行排序,所以在添加、刪除、查找數(shù)據(jù)的時候,都會使用二分法查找,只適合于小數(shù)據(jù)量操作,如果在數(shù)據(jù)量比較大的情況下,那么它的性能將退化。而HashMap內(nèi)部則是數(shù)組+鏈表結(jié)構(gòu),所以在數(shù)據(jù)量較少的時候,HashMap的Entry Array比ArrayMap占用更多的內(nèi)存。因為使用Bundle的場景大多數(shù)為小數(shù)據(jù)量,我沒見過在兩個Activity之間傳遞10個以上數(shù)據(jù)的場景,所以相比之下,在這種情況下使用 ArrayMap保存數(shù)據(jù),在操作速度和內(nèi)存占用上都具有優(yōu)勢,因此使用Bundle來傳遞數(shù)據(jù),可以保 證更快的速度和更少的內(nèi)存占用。
另外一個原因,則是在Android中如果使用Intent來攜帶數(shù)據(jù)的話,需要數(shù)據(jù)是基本類型或者是可序列化類型,HashMap使用Serializable進(jìn)行序列化,而Bundle則是使用Parcelable進(jìn)行序列化。 而在Android平臺中,更推薦使用Parcelable實現(xiàn)序列化,雖然寫法復(fù)雜,但是開銷更小,所以為了更加快速的進(jìn)行數(shù)據(jù)的序列化和反序列化,系統(tǒng)封裝了Bundle類,方便我們進(jìn)行數(shù)據(jù)的傳輸。
-
Android中Intent/Bundle的通信原理及大小限制?
Android 中的 bundle 實現(xiàn)了 parcelable 的序列化接口,目的是為了在進(jìn)程間進(jìn)行通訊。不同的進(jìn)程共享一片固定大小的內(nèi)存。parcelable 利用 parcel 對象的 read/write 方法,對需要傳遞的數(shù)據(jù)進(jìn)行內(nèi)存讀寫,因此這一塊共享內(nèi)存不能過大,在利用 bundle 進(jìn)行傳輸時,會初始化一個 BINDER_VM_SIZE 的大小 = 1 * 1024 * 1024 - 4096 * 2,即便通過 修改 Framework 的代碼,bundle 內(nèi)核的映射只有 4M,最大只能擴(kuò)展到 4M。
-
為何Intent不能直接在組件間傳遞對象而要通過序列化機(jī)制?
因為 Activity 啟動過程是需要與 AMS 交互,AMS 與 UI 進(jìn)程是不同一個的,因此進(jìn)程間需要交互數(shù)據(jù),就必須序列化。
更詳細(xì)的結(jié)識需要結(jié)合AMS。