讀書筆記——Intent資料傳輸(Parcelable和Serializable)
putExtra()
最常用的Intent資料傳輸方式,簡單,在獲取的時候getIntent().getXXXExtra(),根據鍵值對獲取到對應的值。簡單方便。
缺點:
所支援的型別有限,比如不可傳遞自定義物件等。
傳遞物件——Serializable方式
簡介
序列化:表示將一個物件轉換成可儲存或可傳輸的狀態。序列化的物件可以在網路上進行傳輸,也可以儲存到本地。
用法——讓一個類去實現Serializable
讓某個類去實現Serializable介面,該類所有物件都是可序列化的實現。
實現
public class Person implements Serializable{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this .age = age;
}
}
//傳輸
Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("person_data", person);
startActivity(intent);
//獲取
Person person = (Person) getIntent().getSerializableExtra("person_data" );
傳遞物件——Parcelable方式
原理
將一個完整的物件進行分解,分解後的每一部分都是Intent所支援的型別。
實現
public class Person implements Parcelable {
private String name;
private int age;
……
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name); // 寫出name
dest.writeInt(age); // 寫出age
}
public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
Person person = new Person();
person.name = source.readString(); // 讀取name
person.age = source.readInt(); // 讀取age
return person;
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}
首先我們讓 Person 類去實現了Parcelable 介面,這樣就必須重寫 describeContents()和 writeToParcel()這兩個方法。其中describeContents()方法直接返回 0 就可以了,而 writeToParcel()方法中我們需要呼叫 Parcel的 writeXxx()方法將 Person 類中的欄位一一寫出。注意字串型資料就呼叫 writeString()方法,整型資料就呼叫 writeInt()方法,以此類推。
除此之外,我們還必須在 Person 類中提供一個名為 CREATOR 的常量,這裡建立了Parcelable.Creator 介面的一個實現,並將泛型指定為 Person。接著需要重寫 createFromParcel()和 newArray()這兩個方法,在 createFromParcel()方法中我們要去讀取剛才寫出的 name 和 age欄位,並建立一個 Person物件進行返回,其中 name 和 age 都是呼叫 Parcel的 readXxx()方法讀取到的,注意這裡讀取的順序一定要和剛才寫出的順序完全相同。而 newArray()方法中的實現就簡單多了,只需要 new 出一個 Person 陣列,並使用方法中傳入的 size 作為陣列大小就可以了。
//傳輸
Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("person_data", person);
startActivity(intent);
或者
通過將一個物件放到一個bundle裡面然後呼叫Bundle#writeToParcel(Parcel, int)方法來模擬傳遞物件給一個activity的過程,然後再把這個物件取出來。
//獲取
Person person = (Person) getIntent().getParcelableExtra("person_data");
- 整個讀寫全是在記憶體中進行,主要是通過malloc()、realloc()、memcpy()等記憶體操作進行,所以效率比JAVA序列化中使用外部儲存器會高很多;
- 讀寫時是4位元組對齊的,可以看到#define PAD_SIZE(s) (((s)+3)&~3)這句巨集定義就是在做這件事情;
- 如果預分配的空間不夠時newSize = ((mDataSize+len)*3)/2;會一次多分配50%;
- 對於普通資料,使用的是mData記憶體地址,對於IBinder型別的資料以及FileDescriptor使用的是mObjects記憶體地址。後者是通過flatten_binder()和unflatten_binder()實現的,目的是反序列化時讀出的物件就是原物件而不用重新new一個新物件。
Parcelable和Serializable的區別
Serializable的作用是為了儲存物件的屬性到本地檔案、資料庫、網路流、rmi以方便資料傳輸,當然這種傳輸可以是程式內的也可以是兩個程式間的。而Android的Parcelable的設計初衷是因為Serializable效率過慢,為了在程式內不同元件間以及不同Android程式間(AIDL)高效的傳輸資料而設計,這些資料僅在記憶體中存在,Parcelable是通過IBinder通訊的訊息的載體。
2、儲存媒介
Serializable使用IO讀寫儲存在硬碟上,而Parcelable是直接在記憶體中讀寫,很明顯記憶體的讀寫速度通常大於IO讀寫,所以在Android中通常優先選擇Parcelable。
3、優缺點
(1)、 serializable的迷人之處在於你只需要對某個類以及它的屬性實現Serializable 介面即可。Serializable 介面是一種標識介面(marker interface),這意味著無需實現方法,Java便會對這個物件進行高效的序列化操作。
這種方法的缺點是使用了反射,序列化的過程較慢。這種機制會在序列化的時候建立許多的臨時物件,容易觸發垃圾回收。
(2)、程式碼將會執行地特別快。原因之一就是我們已經清楚地知道了序列化的過程,而不需要使用反射來推斷。同時為了更快地進行序列化,物件的程式碼也需要高度優化。
因此,很明顯實現Parcelable並不容易。實現Parcelable介面需要寫大量的模板程式碼,這使得物件程式碼變得難以閱讀和維護。
總結
但是大多數情況下, Serializable 的龜速不會太引人注目。你想偷點懶就用它吧,不過要記得serialization是一個比較耗資源的操作,儘量少使用。
如果你想要傳遞一個包含許多物件的列表,那麼整個序列化的過程的時間開銷可能會超過一秒,這會讓螢幕轉向的時候變得很卡頓。