Parcel
官方文档的语义
- 是消息的容器。消息包括,数据和对象的引用。
- Parcel是android 系统特有的一套机制,可以将序列化之后的数据写入一个共享内存中,其他进程通过Parcel可以从这块共享内存读出字节流,并反序列化成对象。
- linux系统 中 每个进程 都包括(自己本身的内存空间+部分共享内存空间),所以不同进程可以通过这段共享内存进行传递Parcel 打包好的数据(IPC过程)。
使用
- Parcel.obtain() 获取
//Parcel.java
private static final int POOL_SIZE = 6;
//sOwnedPool 维持着一个长度为6的 Parcel 数组
private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
//是否是自己通过natvie 方法创建的,如果是自己创建的话,可以自己控制自己的生命周期。
//因为obtain 方法获取时,可能获取的是已经存在的 Parcel 对象
private boolean mOwnsNativeParcelObject;
@SuppressWarnings({"UnusedDeclaration"})
//可以理解为指向的指针
private long mNativePtr; // used by native code
//获取
public static Parcel obtain() {
final Parcel[] pool = sOwnedPool;
//上对象锁
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
//遍历 sOwnedPool 数组,将不为null的 Parcel对象,赋给p,并返回
if (p != null) {
//将数组中的该位置对象,置为null
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
return p;
}
}
}
//如果sOwnedPool 数组中,不存在 Parcel对象 ,则新建
return new Parcel(0);
}
private Parcel(long nativePtr) {
if (DEBUG_RECYCLE) {
mStack = new RuntimeException();
}
//Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
init(nativePtr);
}
private void init(long nativePtr) {
if (nativePtr != 0) {
mNativePtr = nativePtr;
mOwnsNativeParcelObject = false;
} else {
// nativePtr=0
//通过Native方法,创建了 Parcel 对象,并且被 mNativePtr 指针指向
mNativePtr = nativeCreate();
mOwnsNativeParcelObject = true;
}
}
- Parcel.recycle(); 回收
//Parcel.java
/**
* Put a Parcel object back into the pool. You must not touch
* the object after this call.
*/
public final void recycle() {
if (DEBUG_RECYCLE) mStack = null;
//通过native 方法,释放对象资源
freeBuffer();
final Parcel[] pool;
if (mOwnsNativeParcelObject) {
pool = sOwnedPool;
} else {
mNativePtr = 0;
pool = sHolderPool;
}
synchronized (pool) {
//遍历存放Parcel 的数组,并将 Parcel 对象赋给 数组第一个为空的位置
for (int i=0; i<POOL_SIZE; i++) {
if (pool[i] == null) {
pool[i] = this;
return;
}
}
}
}
- Parcel 写入 (读出) 对象
-
支持基本数据类型
writeByte(byte), readByte(), writeDouble(double), readDouble(), writeFloat(float), readFloat(), writeInt(int), readInt(), writeLong(long), readLong(), writeString(String), readString() -
支持数组类型
writeBooleanArray(boolean[]), readBooleanArray(boolean[]), createBooleanArray()
writeByteArray(byte[]), writeByteArray(byte[], int, int), readByteArray(byte[]), createByteArray()
writeCharArray(char[]), readCharArray(char[]), createCharArray()
writeDoubleArray(double[]), readDoubleArray(double[]), createDoubleArray()
writeFloatArray(float[]), readFloatArray(float[]), createFloatArray()
writeIntArray(int[]), readIntArray(int[]), createIntArray()
writeLongArray(long[]), readLongArray(long[]), createLongArray()
writeStringArray(String[]), readStringArray(String[]), createStringArray().
writeSparseBooleanArray(SparseBooleanArray), readSparseBooleanArray(). -
支持实现了Parcelable 接口的对象
writeParcelable(Parcelable, int) writeParcelableArray(ClassLoader) ,writeParcelableCreator (Parcelable p) writeParcelableList(List val, int flags),包括对应的read 方法。 -
支持Binder 对象(跨进程通讯)
-
支持 FileDescriptor (原始的Linux文件描述符)
以上总要分析了Parcel java 层的源码,至于底层的实现,都是通过c/c++层native 方法,如果想深入了解可以
Parcelable
提起序列化, 我们立马会想到 1.Parcelabel 和 2. Serializable
1. Serializable接口 :
java 序列化的一种手段,它是通过IO 操作,将对象写入文件(序列化),或从文件中读出(反序列化)。并且,通过 serialVersionUID(序列化时,一起写入文件), 去判断是否反序列化成功。 注:transient 关键字修饰的 字段 不参与序列化。
2. Parcelabel接口
实现 Parcelabel 接口的对象,内部通过 Parcel 的写入 和 读出完成序列化的
//Parcelable.java
//返回当前对象内容的描述符。如果含有文件的描述符,返回1,否则返回0.
//平时使用中,基本都是默认返回0
public @ContentsFlags int describeContents()
//将对象写入 Parcel 对象dest中, flags=1,代表当前对象需要作为返回值返回,不能立即释放资源,平时使用,基本为0
public void writeToParcel(Parcel dest, @WriteFlags int flags);
User,Book实例
/**
* @des :
* @author: lht
* @time : 2019/10/11 17:49
*/
public class Book implements Parcelable {
private String bookName;
private String bookPrice;
protected Book(Parcel in) {
bookName = in.readString();
bookPrice = in.readString();
}
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) {
dest.writeString(bookName);
dest.writeString(bookPrice);
}
}
/**
* @des :
* @author: lht
* @time : 2019/10/11 16:08
*/
public class Student implements Parcelable {
private String name;
private int age;
private Book book;
protected Student(Parcel in) {
name = in.readString();
age = in.readInt();
book = in.readParcelable(Book.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
dest.writeParcelable(book, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Student> CREATOR = new Creator<Student>() {
@Override
public Student createFromParcel(Parcel in) {
return new Student(in);
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
- createFromParcel :从序列化后的对象中创建原始对象
- newArray :创建指定长度的原始对象数组
- Student(Parcel in) :从序列化后的对象中创建原始对象
- book = in.readParcelable(Book.class.getClassLoader());
Student 对象 中,由于book是另外一个序列化对象,所以book的反序列化需要传递当前线程的上下文类加载器,否则无法找到book类
总结
- Serializable 比 Parcelable 使用起来方便。但是,现在的AS 可以自动生成实现 Parcelable 接口要 实现的方法,所以可以忽略。
- 序列和反序列的过程中,Serializable 是通过频繁的IO操作来完成,而Parcelable 是由Parcel 通过 共享内存空间,来完成的。所以 Parcelable 的效率要比 Serializable 高很多。因此在Intent 中,使用Parcelable 最优。
- 由于 Parcelable不能很好的保证数据的持续性在外界有变化的情况下。所以,尽管Serializable效率低点,但 在数据存储在磁盘 或网络传输中,使用Serializable 最优 。