Activity之間傳資料時,為了避免麻煩,往往會將一些值封裝成對象,然後將整個對象傳遞過去。傳對象的時候有兩種情況,一種是實現Parcelable介面,一種是實現Serializable介面。
0、解釋兩種介面:
1)實現Serializable介面是JavaSE本身就支援的。
2)Parcelable是Android特有的功能,效率比實現Serializable介面高,像用於Intent資料傳遞也都支援,而且還可以用在處理序間通訊(IPC),除了基本類型外,只有實現了Parcelable介面的類才能被放入Parcel中。
3)什麼時候使用序列化?
a)當你想把的記憶體中的對象寫入到硬碟的時候;
b)當你想用通訊端在網路上傳送對象的時候;
c)當你想通過RMI傳輸對象的時候;
再稍微解釋一下:a)比如說你的記憶體不夠用了,那電腦就要將記憶體裡面的一部分對象暫時的儲存到硬碟中,等到要用的時候再讀入到記憶體中,硬碟的那部分儲存空間就是所謂的虛擬記憶體。在比如過你要將某個特定的對象儲存到檔案中,我隔幾天在把它拿出來用,那麼這時候就要實現Serializable介面;
b)在進行java的Socket編程的時候,你有時候可能要傳輸某一類的對象,那麼也就要實現Serializable介面;最常見的你傳輸一個字串,它是JDK裡面的類,也實現了Serializable介面,所以可以在網路上傳輸。
c)如果要通過遠端方法調用(RMI)去調用一個遠程對象的方法,如在電腦A中調用另一台電腦B的對象的方法,那麼你需要通過JNDI服務擷取電腦B目標對象的引用,將對象從B傳送到A,就需要實現序列化介面。
1、什麼是Parcelable介面呢?
1)Parcelable,定義了將資料寫入Parcel,和從Parcel中讀出的介面。一個實體(用類來表示),如果需要封裝到訊息中去,就必須實現這一介面,實現了這一介面,該實體就成為“可打包的”了。
2)Parcelable介面的定義:
public interface Parcelable {
//內容描述介面,基本不用管
public int describeContents();
//寫入介面函數,打包
public void writeToParcel(Parcel dest, int flags);
//讀取介面,目的是要從Parcel中構造一個實現了Parcelable的類的執行個體處理。因為實作類別在這裡還是不可知的,所以需要用到模板的方式,繼承類名通過模板參數傳入。
//為了能夠實現模板參數的傳入,這裡定義Creator嵌入介面,內含兩個介面函數分別返回單個和多個繼承類執行個體。
public interface Creator<T> {
public T createFromParcel(Parcel source);
public T[] newArray(int size);
}
}
3)怎麼實現Parcelable介面?
從parcelable介面定義中,我們可以看到,實現parcelable介面,需要我們實現下面幾個方法:
(1.)describeContents方法。內容介面描述,預設返回0就可以;
(2.)writeToParcel 方法。該方法將類的資料寫入外部提供的Parcel中.即打包需要傳遞的資料到Parcel容器儲存,以便從parcel容器擷取資料,該方法聲明如下:
writeToParcel (Parcel dest, int flags) 具體參數含義見doc文檔
(3.)靜態Parcelable.Creator介面,本介面有兩個方法:
createFromParcel(Parcel in) 從Parcel容器中讀取傳遞資料值,封裝成Parcelable對象返回邏輯層。
newArray(int size) 建立一個類型為T,長度為size的數組,僅一句話(return new T[size])即可。方法是供外部類還原序列化本類數組使用。
4)代碼實現>
(1.)實現MyParcelable類:
package com.jyxp.my.parcelable;
import java.util.ArrayList;
import java.util.List;
import android.os.Parcel;
import android.os.Parcelable;
public class MyParcelable implements Parcelable {
private int mInteger;
private MyParcelable2 mParcelable;
private List<MyParcelable2> myParcelable2s = new ArrayList<MyParcelable2>();
private MySerializable mMySerializable;
public MyParcelable() {
// TODO Auto-generated constructor stub
}
@SuppressWarnings("unchecked")
public MyParcelable(Parcel in) {
// TODO Auto-generated constructor stub
mInteger = in.readInt();
mParcelable = in.readParcelable(MyParcelable2.class.getClassLoader()); //這個地方的ClassLoader不能為null
myParcelable2s = in.readArrayList(MyParcelable2.class.getClassLoader());
mMySerializable = (MySerializable) in.readSerializable();
}
public int getmInteger() {
return mInteger;
}
public void setmInteger(int mInteger) {
this.mInteger = mInteger;
}
public MyParcelable2 getmParcelable() {
return mParcelable;
}
public void setmParcelable(MyParcelable2 mParcelable) {
this.mParcelable = mParcelable;
}
public List<MyParcelable2> getMyParcelable2s() {
return myParcelable2s;
}
public void setMyParcelable2s(List<MyParcelable2> myParcelable2s) {
this.myParcelable2s = myParcelable2s;
}
public MySerializable getmMySerializable() {
return mMySerializable;
}
public void setmMySerializable(MySerializable mMySerializable) {
this.mMySerializable = mMySerializable;
}
@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeInt(mInteger);
dest.writeParcelable(mParcelable, flags);
dest.writeList(myParcelable2s);
dest.writeSerializable(mMySerializable);
}
public static final Parcelable.Creator<MyParcelable> CREATOR = new Creator<MyParcelable>() {
@Override
public MyParcelable[] newArray(int size) {
// TODO Auto-generated method stub
return new MyParcelable[size];
}
@Override
public MyParcelable createFromParcel(Parcel source) {
// TODO Auto-generated method stub
return new MyParcelable(source);
}
};
}
注意:
1、必須實現Parcelable.Creator介面,並且存取控制必須是public!!;實現Parcelable.Creator介面對象名必須為CREATOR!!否則在擷取資料的時候,會報錯,如下:android.os.BadParcelableException:
2、在讀取Parcel容器裡的資料時,必須按成員變數聲明的順序讀取資料,不然會出現擷取資料出錯。
3、注意Parcel out和in對應的屬性順序不能錯,否則得不到值;如果想傳遞的值沒有write和read,同樣也是擷取不到值的。
4、實現Parcelable介面我採用的做法是,從android API文檔中找到Parcelable介面,詳見Android 文檔,將demo copy,然後將MyParcelable類名全部替換,再填寫write和read的值。在填寫值的時候上面的代碼給出了一些樣本,有時候會出現以下錯誤:
(1、)Caused By:android.os.BadParcelableException: ClassNotFoundException when unmarshalling,原因是ClassLoader設定不對,或者沒有傳入ClassLoader。
(2、)java.lang.RuntimeException: Parcelable encountered IOException writing serializable object,原因是傳遞的Parcelable對象裡面的對象也要Parcelable或者Serializable。
2、什麼是Serializable介面?
1)一個對象序列化的介面,一個類只有實現了Serializable介面,它的對象才是可序列化的。因此如果要序列化某些類的對象,這些類就必須實現Serializable介面。而實際上,Serializable是一個空介面,沒有什麼具體內容,它的目的只是簡單的標識一個類的對象可以被序列化。
2)如何?Serializable介面?
很簡單,只要implements Serializable介面就可以了
3)代碼實現>
package com.jyxp.my.parcelable;
import java.io.Serializable;
public class MySerializable implements Serializable {
private static final long serialVersionUID = 1L;
private Double mDouble;
private Float mFloat;
public MySerializable() {
// TODO Auto-generated constructor stub
}
public Double getmDouble() {
return mDouble;
}
public void setmDouble(Double mDouble) {
this.mDouble = mDouble;
}
public Float getmFloat() {
return mFloat;
}
public void setmFloat(Float mFloat) {
this.mFloat = mFloat;
}
}
3、如何?傳值
1)基礎資料型別 (Elementary Data Type),自身可以
2)傳遞Serializable對象時,被傳遞的Serializable對象裡面的自訂成員對象(非API中的Serializable對象)也要實現Serializable介面,否則會出現Caused
by: java.io.NotSerializableException異常。從上面的代碼可以看出,在Parcelable對象中是可以傳遞Serializable對象的,但Serializable對象裡面傳遞的時候可不可以有Parcelable?回答是否定的,一樣會產生java.io.NotSerializableException異常.
3)android api中只能傳遞Parcelable對象的集合,而不能傳遞Serializable對象的集合,也就是只能傳遞ArrayList<Bitmap>,卻不能傳遞ArrayList<Designer>。剛剛開始學android的時候,對象都是被封裝成Serializable,再傳遞,因為Serializable是JAVASE裡面的本地化介面,很熟悉,當時也產生疑問,為什麼會有Parcelable介面,這兩個有什麼區別?到後來,當Serializable不能滿足要求的時候就明白了,android利用Pacelable對自己的東西進行封裝,就像Worker中的Bitmap,在read的時候可以不需要設定ClassLoader。
4)也是可以傳遞枚舉enum的,把枚舉當做類來看就行了。