[轉(zhuǎn)]Android中Parcelable的原理和使用方法

Parcelable的簡(jiǎn)單介紹

介紹Parcelable不得不先提一下Serializable搓译,Serializable是Java為我們提供的一個(gè)標(biāo)準(zhǔn)化的序列化接口,而Parcelable是Android為我們提供的序列化的接口

進(jìn)行Android開(kāi)發(fā)的時(shí)候置鼻,無(wú)法將對(duì)象的引用傳給Activities或者Fragments,我們需要將這些對(duì)象放到一個(gè)Intent或者Bundle里面叶雹,然后再傳遞。簡(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)稱(chēng)快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)類(lèi)是可以通過(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ò)程茬射。

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

什么是序列化?序列化,表示將一個(gè)對(duì)象轉(zhuǎn)換成可存儲(chǔ)或可傳輸?shù)臓顟B(tài)冒签。序列化后的對(duì)象可以在網(wǎng)絡(luò)上進(jìn)行傳輸在抛,也可以存儲(chǔ)到本地。接下來(lái)在實(shí)現(xiàn)Parcelable之前,介紹下實(shí)現(xiàn)Parcelable的三大流程

public class Album implements Parcelable {
 
    /**
     * 負(fù)責(zé)反序列化
     */
    public static final Creator<Album> CREATOR = new Creator<Album>() {
        /**
         * 從序列化對(duì)象中镣衡,獲取原始的對(duì)象
         * @param source
         * @return
         */
        @Override
        public Album createFromParcel(Parcel source) {
            return new Album(source);
        }
 
        /**
         * 創(chuàng)建指定長(zhǎng)度的原始對(duì)象數(shù)組
         * @param size
         * @return
         */
        @Override
        public Album[] newArray(int size) {
            return new Album[0];
        }
    };
 
 
 
    private final String mId;
    private final String mCoverPath;
    private final String mDisplayName;
    private final long mCount;
 
 
    Album(String id, String coverPath, String displayName, long count) {
        mId = id;
        mCoverPath = coverPath;
        mDisplayName = displayName;
        mCount = count;
    }
 
    Album(Parcel source) {
        mId = source.readString();
        mCoverPath = source.readString();
        mDisplayName = source.readString();
        mCount = source.readLong();
    }
 
    /**
     * 描述
     * 返回的是內(nèi)容的描述信息
     * 只針對(duì)一些特殊的需要描述信息的對(duì)象,需要返回1,其他情況返回0就可以
     *
     * @return
     */
    @Override
    public int describeContents() {
        return 0;
    }
 
    /**
     * 序列化
     *
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mId);
        dest.writeString(mCoverPath);
        dest.writeString(mDisplayName);
        dest.writeLong(mCount);
    }
 

實(shí)現(xiàn)Parcelable的作用
(1)永久性保存對(duì)象霜定,保存對(duì)象的字節(jié)序列到本地文件中;
(2)通過(guò)序列化對(duì)象在網(wǎng)絡(luò)中傳遞對(duì)象廊鸥;
(3)通過(guò)序列化在進(jìn)程間傳遞對(duì)象望浩。

首先寫(xiě)一個(gè)類(lèi)實(shí)現(xiàn)Parcelable接口,會(huì)讓我們實(shí)現(xiàn)兩個(gè)方法:

describeContents 描述
其中describeContents就是負(fù)責(zé)文件描述.通過(guò)源碼的描述可以看出,只針對(duì)一些特殊的需要描述信息的對(duì)象,需要返回1,其他情況返回0就可以

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

我們將上面的Album對(duì)象實(shí)現(xiàn)序列化,Album對(duì)象包含四個(gè)字段。

反序列化

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

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

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

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

import android.os.Parcel;
import android.os.Parcelable;
 
import java.util.ArrayList;
 
 
public class ParcelDemo implements Parcelable {
 
    private int count;
    private String name;
    private ArrayList<String> tags;
    private Book book;
    // ***** 注意: 這里如果是集合 ,一定要初始化 *****
    private ArrayList<Book> books = new ArrayList<>();
 
 
    /**
     * 反序列化
     *
     * @param in
     */
    protected ParcelDemo(Parcel in) {
        count = in.readInt();
        name = in.readString();
        tags = in.createStringArrayList();
 
        // 讀取對(duì)象需要提供一個(gè)類(lèi)加載器去讀取,因?yàn)閷?xiě)入的時(shí)候?qū)懭肓祟?lèi)的相關(guān)信息
        book = in.readParcelable(Book.class.getClassLoader());
 
 
        //讀取集合也分為兩類(lèi),對(duì)應(yīng)寫(xiě)入的兩類(lèi)
 
        //這一類(lèi)需要用相應(yīng)的類(lèi)加載器去獲取
        in.readList(books, Book.class.getClassLoader());// 對(duì)應(yīng)writeList
 
 
        //這一類(lèi)需要使用類(lèi)的CREATOR去獲取
        in.readTypedList(books, Book.CREATOR); //對(duì)應(yīng)writeTypeList
 
        //books = in.createTypedArrayList(Book.CREATOR); //對(duì)應(yīng)writeTypeList
 
 
        //這里獲取類(lèi)加載器主要有幾種方式
        getClass().getClassLoader();
        Thread.currentThread().getContextClassLoader();
        Book.class.getClassLoader();
 
 
    }
 
    public static final Creator<ParcelDemo> CREATOR = new Creator<ParcelDemo>() {
        @Override
        public ParcelDemo createFromParcel(Parcel in) {
            return new ParcelDemo(in);
        }
 
        @Override
        public ParcelDemo[] newArray(int size) {
            return new ParcelDemo[size];
        }
    };
 
    /**
     * 描述
     *
     * @return
     */
    @Override
    public int describeContents() {
        return 0;
    }
 
    /**
     * 序列化
     *
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(count);
        dest.writeString(name);
        //序列化一個(gè)String的集合
        dest.writeStringList(tags);
        // 序列化對(duì)象的時(shí)候傳入要序列化的對(duì)象和一個(gè)flag,
        // 這里的flag幾乎都是0,除非標(biāo)識(shí)當(dāng)前對(duì)象需要作為返回值返回,不能立即釋放資源
        dest.writeParcelable(book, 0);
 
        // 序列化一個(gè)對(duì)象的集合有兩種方式,以下兩種方式都可以
 
 
        //這些方法們把類(lèi)的信息和數(shù)據(jù)都寫(xiě)入Parcel磨德,以使將來(lái)能使用合適的類(lèi)裝載器重新構(gòu)造類(lèi)的實(shí)例.所以效率不高
        dest.writeList(books);
 
 
        //這些方法不會(huì)寫(xiě)入類(lèi)的信息,取而代之的是:讀取時(shí)必須能知道數(shù)據(jù)屬于哪個(gè)類(lèi)并傳入正確的Parcelable.Creator來(lái)創(chuàng)建對(duì)象
        // 而不是直接構(gòu)造新對(duì)象吆视。(更加高效的讀寫(xiě)單個(gè)Parcelable對(duì)象的方法是:
        // 直接調(diào)用Parcelable.writeToParcel()和Parcelable.Creator.createFromParcel())
        dest.writeTypedList(books);
 
 
    }
}

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

import android.os.Parcel;
import android.os.Parcelable;
 
 
public class Book implements Parcelable {
 
    protected Book(Parcel in) {
    }
 
    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];
        }
    };
 
    @Override
    public int describeContents() {
        return 0;
    }
 
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    }
}

寫(xiě)入和讀取集合有兩種方式,
一種是寫(xiě)入類(lèi)的相關(guān)信息,然后通過(guò)類(lèi)加載器去讀取, –> writeList | readList
二是不用類(lèi)相關(guān)信息,創(chuàng)建時(shí)傳入相關(guān)類(lèi)的CREATOR來(lái)創(chuàng)建 –> writeTypeList | readTypeList | createTypedArrayList
第二種效率高一些
一定要注意如果有集合定義的時(shí)候一定要初始化 like this –>
public ArrayList<T> demo = new ArrayList<>();

舉個(gè)詳細(xì)的例子:

class MessageFilter() : Parcelable{
 
    var sender: List<FilterSender>? = null
 
    var messageType: FilterMessageType? = null
 
    var time: FilterTime? = null
 
    //讀數(shù)據(jù)進(jìn)行恢復(fù)
    constructor(parcel: Parcel) : this() {
        this.sender = mutableListOf()
        parcel.readList(sender, FilterSender.javaClass.classLoader)
        messageType = parcel.readParcelable(FilterMessageType.javaClass.classLoader)
        time = parcel.readParcelable(FilterTime.javaClass.classLoader)
    }
 
    //寫(xiě)數(shù)據(jù)進(jìn)行保存
    override fun writeToParcel(dest: Parcel?, flags: Int) {
        dest?.writeList(sender)
        dest?.writeParcelable(messageType, Parcelable.PARCELABLE_WRITE_RETURN_VALUE)
        dest?.writeParcelable(time, Parcelable.PARCELABLE_WRITE_RETURN_VALUE)
    }
 
    override fun describeContents(): Int {
        return 0
    }
 
    companion object CREATOR : Parcelable.Creator<MessageFilter> {
        override fun createFromParcel(parcel: Parcel): MessageFilter {
            return MessageFilter(parcel)
        }
 
        override fun newArray(size: Int): Array<MessageFilter?> {
            return arrayOfNulls(size)
        }
    }
}
 
//涉及到的相關(guān)類(lèi)都需要實(shí)現(xiàn)Parcelable接口
data class FilterSender(val type: String, val id: String): Parcelable {
 
    constructor(parcel: Parcel) : this(
        parcel.readString(),
        parcel.readString())
 
    override fun writeToParcel(dest: Parcel?, flags: Int) {
        dest?.writeString(type)
        dest?.writeString(id)
    }
 
    override fun describeContents(): Int {
        return 0
    }
 
    companion object CREATOR : Parcelable.Creator<FilterSender> {
        override fun createFromParcel(parcel: Parcel): FilterSender {
            return FilterSender(parcel)
        }
 
        override fun newArray(size: Int): Array<FilterSender?> {
            return arrayOfNulls(size)
        }
    }
}

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ù)雜)

選擇序列化方法的原則

1)在使用內(nèi)存的時(shí)候啦吧,Parcelable比Serializable性能高您觉,所以推薦使用Parcelable。

2)Serializable在序列化的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量授滓,從而引起頻繁的GC琳水。

3)Parcelable不能使用在要將數(shù)據(jù)存儲(chǔ)在磁盤(pán)上的情況肆糕,因?yàn)镻arcelable不能很好的保證數(shù)據(jù)的持續(xù)性在外界有變化的情況下。盡管Serializable效率低點(diǎn)在孝,但此時(shí)還是建議使用Serializable 诚啃。

延伸:

下面介紹下ParcelableSerializable的作用、效率私沮、區(qū)別及選擇始赎。

1、作用

Serializable的作用是為了保存對(duì)象的屬性到本地文件仔燕、數(shù)據(jù)庫(kù)造垛、網(wǎng)絡(luò)流、rmi以方便數(shù)據(jù)傳輸晰搀,當(dāng)然這種傳輸可以是程序內(nèi)的也可以是兩個(gè)程序間的筋搏。而Android的Parcelable的設(shè)計(jì)初衷是因?yàn)?code>Serializable效率過(guò)慢,為了在程序內(nèi)不同組件間以及不同Android程序間(AIDL)高效的傳輸數(shù)據(jù)而設(shè)計(jì)厕隧,這些數(shù)據(jù)僅在內(nèi)存中存在奔脐,Parcelable是通過(guò)IBinder通信的消息的載體。

從上面的設(shè)計(jì)上我們就可以看出優(yōu)劣了吁讨。

2髓迎、效率及選擇

Parcelable的性能比Serializable好,在內(nèi)存開(kāi)銷(xiāo)方面較小建丧,所以在內(nèi)存間數(shù)據(jù)傳輸時(shí)推薦使用Parcelable排龄,如activity間傳輸數(shù)據(jù),而Serializable可將數(shù)據(jù)持久化方便保存翎朱,所以在需要保存或網(wǎng)絡(luò)傳輸數(shù)據(jù)時(shí)選擇Serializable橄维,因?yàn)閍ndroid不同版本Parcelable可能不同,所以不推薦使用Parcelable進(jìn)行數(shù)據(jù)持久化拴曲。

3争舞、編程實(shí)現(xiàn)

對(duì)于Serializable,類(lèi)只需要實(shí)現(xiàn)Serializable接口澈灼,并提供一個(gè)序列化版本id(serialVersionUID)即可竞川。Parcelable則需要實(shí)現(xiàn)writeToParceldescribeContents函數(shù)以及靜態(tài)的CREATOR變量叁熔,實(shí)際上就是將如何打包和解包的工作自己來(lái)定義委乌,而序列化的這些操作完全由底層實(shí)現(xiàn)。

4荣回、高級(jí)功能上

Serializable序列化不保存靜態(tài)變量遭贸,可以使用Transient關(guān)鍵字對(duì)部分字段不進(jìn)行序列化,也可以覆蓋writeObject心软、readObject方法以實(shí)現(xiàn)序列化過(guò)程自定義壕吹。

轉(zhuǎn)自:https://blog.csdn.net/jdsjlzx/article/details/109064067

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末除秀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子算利,更是在濱河造成了極大的恐慌,老刑警劉巖泳姐,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件效拭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡胖秒,警方通過(guò)查閱死者的電腦和手機(jī)缎患,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)阎肝,“玉大人挤渔,你說(shuō)我怎么就攤上這事》缣猓” “怎么了判导?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)沛硅。 經(jīng)常有香客問(wèn)我眼刃,道長(zhǎng),這世上最難降的妖魔是什么摇肌? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任擂红,我火速辦了婚禮,結(jié)果婚禮上围小,老公的妹妹穿的比我還像新娘昵骤。我一直安慰自己,他們只是感情好肯适,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布变秦。 她就那樣靜靜地躺著,像睡著了一般框舔。 火紅的嫁衣襯著肌膚如雪伴栓。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,604評(píng)論 1 305
  • 那天雨饺,我揣著相機(jī)與錄音钳垮,去河邊找鬼。 笑死额港,一個(gè)胖子當(dāng)著我的面吹牛饺窿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播移斩,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肚医,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼绢馍!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起肠套,我...
    開(kāi)封第一講書(shū)人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤舰涌,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后你稚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瓷耙,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年刁赖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搁痛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宇弛,死狀恐怖鸡典,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情枪芒,我是刑警寧澤彻况,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站舅踪,受9級(jí)特大地震影響疗垛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜硫朦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一贷腕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咬展,春花似錦泽裳、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)思灰。三九已至嗡髓,卻和暖如春码泛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背裳扯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工抛丽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人饰豺。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓亿鲜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親冤吨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蒿柳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355