詳細(xì)介紹Android中Parcelable的原理和使用方法

今日推薦

經(jīng)常閱讀博客是個(gè)好習(xí)慣
推薦技術(shù)小黑屋的Blog

引言

本篇文章目的在于詳細(xì)的理解Parcelable的使用,而不是死記代碼

我的CSDN博客地址 (由于csdn的圖片經(jīng)常出現(xiàn)不顯示問(wèn)題,以后就在簡(jiǎn)書(shū)寫(xiě)博客了)

特別鳴謝: Android中Parcel的解讀

目錄

  1. Parcelable的簡(jiǎn)單介紹
    1.1 Parcelable的簡(jiǎn)介
    1.2 Parcel的簡(jiǎn)介
    1.3 Parcelable中的三大過(guò)程介紹(序列化,反序列化,描述)
  2. Parcelable的使用和源碼解讀
  3. Parcelable中對(duì)象和集合的處理
  4. Parcelable和Serializable的區(qū)別和比較

1.1 Parcelable的簡(jiǎn)單介紹

介紹Parcelable不得不先提一下Serializable接口,Serializable是Java為我們提供的一個(gè)標(biāo)準(zhǔn)化的序列化接口,那什么是序列化呢? ---- 簡(jiǎn)單來(lái)說(shuō)就是將對(duì)象轉(zhuǎn)換為可以傳輸?shù)亩M(jìn)制流(二進(jìn)制序列)的過(guò)程,這樣我們就可以通過(guò)序列化,轉(zhuǎn)化為可以在網(wǎng)絡(luò)傳輸或者保存到本地的流(序列),從而進(jìn)行傳輸數(shù)據(jù) ,那反序列化就是從二進(jìn)制流(序列)轉(zhuǎn)化為對(duì)象的過(guò)程.

Parcelable是Android為我們提供的序列化的接口,Parcelable相對(duì)于Serializable的使用相對(duì)復(fù)雜一些,但Parcelable的效率相對(duì)Serializable也高很多,這一直是Google工程師引以為傲的,有時(shí)間的可以看一下Parcelable和Serializable的效率對(duì)比 Parcelable vs Serializable 號(hào)稱快10倍的效率

那我們看一下Android源碼中的Parcelable

    /**
     * Interface for classes whose instances can be written to
   * and restored from a {@link Parcel}.  Classes implementing the Parcelable
 * interface must also have a non-null static field called <code>CREATOR</code>
 * of a type that implements the {@link Parcelable.Creator} interface.
 * 
 * <p>A typical implementation of Parcelable is:</p>
 * 
 * <pre>
 * public class MyParcelable implements Parcelable {
 *     private int mData;
 *
 *       public int describeContents() {
 *         return 0;
 *     }
 *
 *     public void writeToParcel(Parcel out, int flags) {
 *         out.writeInt(mData);
 *     }
 *
 *     public static final Parcelable.Creator<MyParcelable> CREATOR
 *             = new Parcelable.Creator<MyParcelable>() {
 *         public MyParcelable createFromParcel(Parcel in) {
 *             return new MyParcelable(in);
 *         }
 *
 *         public MyParcelable[] newArray(int size) {
 *             return new MyParcelable[size];
 *         }
 *     };
 *     
 *     private MyParcelable(Parcel in) {
 *         mData = in.readInt();
 *     }
 * }</pre>
 */

通過(guò)源碼中的介紹 可以知道,Parcelable接口的實(shí)現(xiàn)類是可以通過(guò)Parcel寫(xiě)入和恢復(fù)數(shù)據(jù)的,并且必須要有一個(gè)非空的靜態(tài)變量 CREATOR, 而且還給了一個(gè)例子,這樣我們寫(xiě)起來(lái)就比較簡(jiǎn)單了,但是簡(jiǎn)單的使用并不是我們的最終目的

通過(guò)查看Android源碼中Parcelable可以看出,Parcelable實(shí)現(xiàn)過(guò)程主要分為序列化,反序列化,描述三個(gè)過(guò)程,下面分別介紹下這三個(gè)過(guò)程

1.2 Parcel的簡(jiǎn)介

在介紹之前我們需要先了解Parcel是什么?Parcel翻譯過(guò)來(lái)是打包的意思,其實(shí)就是包裝了我們需要傳輸?shù)臄?shù)據(jù),然后在Binder中傳輸,也就是用于跨進(jìn)程傳輸數(shù)據(jù)

簡(jiǎn)單來(lái)說(shuō),Parcel提供了一套機(jī)制,可以將序列化之后的數(shù)據(jù)寫(xiě)入到一個(gè)共享內(nèi)存中,其他進(jìn)程通過(guò)Parcel可以從這塊共享內(nèi)存中讀出字節(jié)流,并反序列化成對(duì)象,下圖是這個(gè)過(guò)程的模型。

Parcel模型

Parcel可以包含原始數(shù)據(jù)類型(用各種對(duì)應(yīng)的方法寫(xiě)入,比如writeInt(),writeFloat()等),可以包含Parcelable對(duì)象,它還包含了一個(gè)活動(dòng)的IBinder對(duì)象的引用,這個(gè)引用導(dǎo)致另一端接收到一個(gè)指向這個(gè)IBinder的代理IBinder。

Parcelable通過(guò)Parcel實(shí)現(xiàn)了read和write的方法,從而實(shí)現(xiàn)序列化和反序列化,我們?cè)谠创a中看一下

Parcel中的Read方法
Parcel中的write方法

可以看出包含了各種各樣的read和write方法,最終都是通過(guò)native方法實(shí)現(xiàn)

Parcel中的native方法

1.3 Parcelable中的三大過(guò)程介紹(序列化,反序列化,描述)

到這里,基本上關(guān)系都理清了,也明白簡(jiǎn)單的介紹和原理了,接下來(lái)在實(shí)現(xiàn)Parcelable之前,介紹下實(shí)現(xiàn)Parcelable的三大流程
首先寫(xiě)一個(gè)類實(shí)現(xiàn)Parcelable接口,會(huì)讓我們實(shí)現(xiàn)兩個(gè)方法

要實(shí)現(xiàn)的方法

1.3.1 描述

其中describeContents就是負(fù)責(zé)文件描述,首先看一下源碼的解讀

描述的源碼

通過(guò)上面的描述可以看出,只針對(duì)一些特殊的需要描述信息的對(duì)象,需要返回1,其他情況返回0就可以

1.3.2 序列化

我們通過(guò)writeToParcel方法實(shí)現(xiàn)序列化,writeToParcel返回了Parcel,所以我們可以直接調(diào)用Parcel中的write方法,基本的write方法都有,對(duì)象和集合比較特殊下面單獨(dú)講,基本的數(shù)據(jù)類型除了boolean其他都有,Boolean可以使用int或byte存儲(chǔ)

舉個(gè)例子:我們將上面的User對(duì)象實(shí)現(xiàn)序列化,User對(duì)象包含三個(gè)字段 age,name,isMale

 /**
 * 該方法負(fù)責(zé)序列化
 * @param dest
 * @param flags
 */
@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(age);
    dest.writeString(name);
    // boolean 可以使用int或byte方式進(jìn)行存儲(chǔ),怎么存就怎么取
    dest.writeInt(isMale ? 1 : 0);
}

1.3.3 反序列化

反序列化需要定義一個(gè)CREATOR的變量,上面也說(shuō)了具體的做法,這里可以直接復(fù)制Android給的例子中的,也可以自己定義一個(gè)(名字千萬(wàn)不能改),通過(guò)匿名內(nèi)部類實(shí)現(xiàn)Parcelable中的Creator的接口

/**
 * 負(fù)責(zé)反序列化
 */
public static final Creator<User> CREATOR = new Creator<User>() {
    /**
     * 從序列化后的對(duì)象中創(chuàng)建原始對(duì)象
     */
    @Override
    public User createFromParcel(Parcel source) {
        return new User(source);
    }

    /**
     * 創(chuàng)建指定長(zhǎng)度的原始對(duì)象數(shù)組
     */
    @Override
    public User[] newArray(int size) {
        return new User[size];
    }
};

public User(Parcel parcel) {
    age = parcel.readInt();
    name = parcel.readString();
    isMale = parcel.readInt() == 1;
}

2. Parcelable的實(shí)現(xiàn)和使用

根據(jù)上面三個(gè)過(guò)程的介紹,Parcelable就寫(xiě)完了,就可以直接在Intent中傳輸了,可以自己寫(xiě)兩個(gè)Activity傳輸一下數(shù)據(jù)試一下,其中一個(gè)putExtra另一個(gè)getParcelableExtra即可

這里實(shí)現(xiàn)Parcelable也很簡(jiǎn)單

1.寫(xiě)一個(gè)類實(shí)現(xiàn)Parcelable然后alt+enter 添加Parcelable所需的代碼塊,AndroidStudio會(huì)自動(dòng)幫我們實(shí)現(xiàn)(這里需要注意如果其中包含對(duì)象或集合需要把對(duì)象也實(shí)現(xiàn)Parcelable)

Paste_Image.png

3. Parcelable中對(duì)象和集合的處理

如果實(shí)現(xiàn)Parcelable接口的對(duì)象中包含對(duì)象或者集合,那么其中的對(duì)象也要實(shí)現(xiàn)Parcelable接口
如果包含對(duì)象Author和集合:

public class Book implements Parcelable {

  public int id;
  public String name;
  public boolean isCell;
  public ArrayList<String> tags;
  public Author author;
  // ***** 注意: 這里如果是集合 ,一定要初始化 *****
  public ArrayList<Author> authors = new ArrayList<>();

  public Book() {
  }
}

這里包含一個(gè)Author對(duì)象和Author集合

首先Author需要先實(shí)現(xiàn)Parcelable,實(shí)現(xiàn)步驟就不貼出來(lái)了,和普通的對(duì)象一樣,實(shí)現(xiàn)三個(gè)過(guò)程

針對(duì)上面Book對(duì)象的序列化和反序列化的實(shí)現(xiàn):

反序列化:

//反序列化
public static final Creator<Book> CREATOR = new Creator<Book>() {
    @Override
    public Book createFromParcel(Parcel in) {
        return new Book(in);
    }

    @Override
    public Book[] newArray(int size) {
        return new Book[size];
    }
};

protected Book(Parcel in) {
    id = in.readInt();
    name = in.readString();
    isCell = in.readByte() != 0;

    tags = in.createStringArrayList();

    // 讀取對(duì)象需要提供一個(gè)類加載器去讀取,因?yàn)閷?xiě)入的時(shí)候?qū)懭肓祟惖南嚓P(guān)信息
    author = in.readParcelable(Author.class.getClassLoader());

    //讀取集合也分為兩類,對(duì)應(yīng)寫(xiě)入的兩類
    //這一類需要用相應(yīng)的類加載器去獲取
    //in.readList(authors, Author.class.getClassLoader()); // 對(duì)應(yīng)writeList

    //這一類需要使用類的CREATOR去獲取
    in.readTypedList(authors, Author.CREATOR); //對(duì)應(yīng)writeTypeList
    //authors = in.createTypedArrayList(Author.CREATOR);//對(duì)應(yīng)writeTypeList

    //這里獲取類加載器主要有幾種方式
    getClass().getClassLoader();
    Thread.currentThread().getContextClassLoader();
    Author.class.getClassLoader();
}

序列化:

@Override
public void writeToParcel(Parcel dest, int flags) {
    //下面三種是基本類型就不多說(shuō)
    dest.writeInt(id);
    dest.writeString(name);
    dest.writeByte((byte) (isCell ? 1 : 0));

    //序列化一個(gè)String的集合
    dest.writeStringList(tags);

    // 序列化對(duì)象的時(shí)候傳入要序列化的對(duì)象和一個(gè)flag,
    // 這里的flag幾乎都是0,除非標(biāo)識(shí)當(dāng)前對(duì)象需要作為返回值返回,不能立即釋放資源
    dest.writeParcelable(author, flags);

    // 序列化一個(gè)對(duì)象的集合有兩種方式,以下兩種方式都可以

    //這些方法們把類的信息和數(shù)據(jù)都寫(xiě)入Parcel,以使將來(lái)能使用合適的類裝載器重新構(gòu)造類的實(shí)例.所以效率不高
    dest.writeList(authors);

    //這些方法不會(huì)寫(xiě)入類的信息,取而代之的是:讀取時(shí)必須能知道數(shù)據(jù)屬于哪個(gè)類并傳入正確的Parcelable.Creator來(lái)創(chuàng)建對(duì)象
    // 而不是直接構(gòu)造新對(duì)象。(更加高效的讀寫(xiě)單個(gè)Parcelable對(duì)象的方法是:
    // 直接調(diào)用Parcelable.writeToParcel()和Parcelable.Creator.createFromParcel())
    dest.writeTypedList(authors);
}

寫(xiě)入和讀取集合有兩種方式,
一種是寫(xiě)入類的相關(guān)信息,然后通過(guò)類加載器去讀取, --> writeList | readList
二是不用類相關(guān)信息,創(chuàng)建時(shí)傳入相關(guān)類的CREATOR來(lái)創(chuàng)建 --> writeTypeList | readTypeList | createTypedArrayList
第二種效率高一些

一定要注意如果有集合定義的時(shí)候一定要初始化 like this -->
public ArrayList<Author> authors = new ArrayList<>();

4. Parcelable和Serializable的區(qū)別和比較

Parcelable和Serializable都是實(shí)現(xiàn)序列化并且都可以用于Intent間傳遞數(shù)據(jù),Serializable是Java的實(shí)現(xiàn)方式,可能會(huì)頻繁的IO操作,所以消耗比較大,但是實(shí)現(xiàn)方式簡(jiǎn)單 Parcelable是Android提供的方式,效率比較高,但是實(shí)現(xiàn)起來(lái)復(fù)雜一些 , 二者的選取規(guī)則是:內(nèi)存序列化上選擇Parcelable, 存儲(chǔ)到設(shè)備或者網(wǎng)絡(luò)傳輸上選擇Serializable(當(dāng)然Parcelable也可以但是稍顯復(fù)雜)

希望這篇文章可以幫助到需要的人,如果還有其他問(wèn)題或者補(bǔ)充可以聯(lián)系我~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容