1. 程式人生 > >讀書筆記——Intent資料傳輸(Parcelable和Serializable)

讀書筆記——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");
  1. 整個讀寫全是在記憶體中進行,主要是通過malloc()、realloc()、memcpy()等記憶體操作進行,所以效率比JAVA序列化中使用外部儲存器會高很多;
  2. 讀寫時是4位元組對齊的,可以看到#define PAD_SIZE(s) (((s)+3)&~3)這句巨集定義就是在做這件事情;
  3. 如果預分配的空間不夠時newSize = ((mDataSize+len)*3)/2;會一次多分配50%;
  4. 對於普通資料,使用的是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是一個比較耗資源的操作,儘量少使用。

如果你想要傳遞一個包含許多物件的列表,那麼整個序列化的過程的時間開銷可能會超過一秒,這會讓螢幕轉向的時候變得很卡頓。