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 诚啃。
延伸:
下面介紹下Parcelable
和Serializable
的作用、效率私沮、區(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)writeToParcel
、describeContents
函數(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