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

Parcelable的簡單介紹

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

進(jìn)行Android開發(fā)的時候,無法將對象的引用傳給Activities或者Fragments,我們需要將這些對象放到一個Intent或者Bundle里面,然后再傳遞。簡單來說就是將對象轉(zhuǎn)換為可以傳輸?shù)亩M(jìn)制流(二進(jìn)制序列)的過程,這樣我們就可以通過序列化,轉(zhuǎn)化為可以在網(wǎng)絡(luò)傳輸或者保存到本地的流(序列),從而進(jìn)行傳輸數(shù)據(jù) ,那反序列化就是從二進(jìn)制流(序列)轉(zhuǎn)化為對象的過程.

Parcelable是Android為我們提供的序列化的接口,Parcelable相對于Serializable的使用相對復(fù)雜一些,但Parcelable的效率相對Serializable也高很多,這一直是Google工程師引以為傲的,有時間的可以看一下Parcelable和Serializable的效率對比 Parcelable vs Serializable 號稱快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>
 */

通過源碼中的介紹 可以知道,Parcelable接口的實現(xiàn)類是可以通過Parcel寫入和恢復(fù)數(shù)據(jù)的,并且必須要有一個非空的靜態(tài)變量 CREATOR,而且還給了一個例子,這樣我們寫起來就比較簡單了,但是簡單的使用并不是我們的最終目的,通過查看Android源碼中Parcelable可以看出,Parcelable實現(xiàn)過程主要分為序列化,反序列化,描述三個過程,下面分別介紹下這三個過程。

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

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

public class Album implements Parcelable {
 
    /**
     * 負(fù)責(zé)反序列化
     */
    public static final Creator<Album> CREATOR = new Creator<Album>() {
        /**
         * 從序列化對象中,獲取原始的對象
         * @param source
         * @return
         */
        @Override
        public Album createFromParcel(Parcel source) {
            return new Album(source);
        }
 
        /**
         * 創(chuàng)建指定長度的原始對象數(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)容的描述信息
     * 只針對一些特殊的需要描述信息的對象,需要返回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);
    }
 

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

首先寫一個類實現(xiàn)Parcelable接口,會讓我們實現(xiàn)兩個方法:

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

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

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

反序列化

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

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

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

Parcelable中對象和集合的處理

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();
 
        // 讀取對象需要提供一個類加載器去讀取,因為寫入的時候?qū)懭肓祟惖南嚓P(guān)信息
        book = in.readParcelable(Book.class.getClassLoader());
 
 
        //讀取集合也分為兩類,對應(yīng)寫入的兩類
 
        //這一類需要用相應(yīng)的類加載器去獲取
        in.readList(books, Book.class.getClassLoader());// 對應(yīng)writeList
 
 
        //這一類需要使用類的CREATOR去獲取
        in.readTypedList(books, Book.CREATOR); //對應(yīng)writeTypeList
 
        //books = in.createTypedArrayList(Book.CREATOR); //對應(yīng)writeTypeList
 
 
        //這里獲取類加載器主要有幾種方式
        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);
        //序列化一個String的集合
        dest.writeStringList(tags);
        // 序列化對象的時候傳入要序列化的對象和一個flag,
        // 這里的flag幾乎都是0,除非標(biāo)識當(dāng)前對象需要作為返回值返回,不能立即釋放資源
        dest.writeParcelable(book, 0);
 
        // 序列化一個對象的集合有兩種方式,以下兩種方式都可以
 
 
        //這些方法們把類的信息和數(shù)據(jù)都寫入Parcel,以使將來能使用合適的類裝載器重新構(gòu)造類的實例.所以效率不高
        dest.writeList(books);
 
 
        //這些方法不會寫入類的信息,取而代之的是:讀取時必須能知道數(shù)據(jù)屬于哪個類并傳入正確的Parcelable.Creator來創(chuàng)建對象
        // 而不是直接構(gòu)造新對象。(更加高效的讀寫單個Parcelable對象的方法是:
        // 直接調(diào)用Parcelable.writeToParcel()和Parcelable.Creator.createFromParcel())
        dest.writeTypedList(books);
 
 
    }
}

Book類,需要先實現(xiàn)Parcelable,實現(xiàn)步驟就不貼出來了,和普通的對象一樣,實現(xiàn)三個過程。

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) {
    }
}

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

舉個詳細(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)
    }
 
    //寫數(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)類都需要實現(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都是實現(xiàn)序列化并且都可以用于Intent間傳遞數(shù)據(jù),Serializable是Java的實現(xiàn)方式,可能會頻繁的IO操作,所以消耗比較大,但是實現(xiàn)方式簡單 Parcelable是Android提供的方式,效率比較高,但是實現(xiàn)起來復(fù)雜一些 , 二者的選取規(guī)則是:內(nèi)存序列化上選擇Parcelable, 存儲到設(shè)備或者網(wǎng)絡(luò)傳輸上選擇Serializable(當(dāng)然Parcelable也可以但是稍顯復(fù)雜)

選擇序列化方法的原則

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

2)Serializable在序列化的時候會產(chǎn)生大量的臨時變量,從而引起頻繁的GC。

3)Parcelable不能使用在要將數(shù)據(jù)存儲在磁盤上的情況,因為Parcelable不能很好的保證數(shù)據(jù)的持續(xù)性在外界有變化的情況下。盡管Serializable效率低點,但此時還是建議使用Serializable 。

延伸:

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

1、作用

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

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

2、效率及選擇

Parcelable的性能比Serializable好,在內(nèi)存開銷方面較小,所以在內(nèi)存間數(shù)據(jù)傳輸時推薦使用Parcelable,如activity間傳輸數(shù)據(jù),而Serializable可將數(shù)據(jù)持久化方便保存,所以在需要保存或網(wǎng)絡(luò)傳輸數(shù)據(jù)時選擇Serializable,因為android不同版本Parcelable可能不同,所以不推薦使用Parcelable進(jìn)行數(shù)據(jù)持久化。

3、編程實現(xiàn)

對于Serializable,類只需要實現(xiàn)Serializable接口,并提供一個序列化版本id(serialVersionUID)即可。Parcelable則需要實現(xiàn)writeToParcel、describeContents函數(shù)以及靜態(tài)的CREATOR變量,實際上就是將如何打包和解包的工作自己來定義,而序列化的這些操作完全由底層實現(xiàn)。

4、高級功能上

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

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

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

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