理解 Parcel 和 Parcelable

Parcel

官方文档的语义

  1. 是消息的容器。消息包括,数据和对象的引用。
  2. Parcel是android 系统特有的一套机制,可以将序列化之后的数据写入一个共享内存中,其他进程通过Parcel可以从这块共享内存读出字节流,并反序列化成对象。
  3. 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 写入 (读出) 对象
  1. 支持基本数据类型
    writeByte(byte), readByte(), writeDouble(double), readDouble(), writeFloat(float), readFloat(), writeInt(int), readInt(), writeLong(long), readLong(), writeString(String), readString()

  2. 支持数组类型
    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().

  3. 支持实现了Parcelable 接口的对象
    writeParcelable(Parcelable, int) writeParcelableArray(ClassLoader) ,writeParcelableCreator (Parcelable p) writeParcelableList(List val, int flags),包括对应的read 方法。

  4. 支持Binder 对象(跨进程通讯)

  5. 支持 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类

总结

  1. Serializable 比 Parcelable 使用起来方便。但是,现在的AS 可以自动生成实现 Parcelable 接口要 实现的方法,所以可以忽略。
  2. 序列和反序列的过程中,Serializable 是通过频繁的IO操作来完成,而Parcelable 是由Parcel 通过 共享内存空间,来完成的。所以 Parcelable 的效率要比 Serializable 高很多。因此在Intent 中,使用Parcelable 最优。
  3. 由于 Parcelable不能很好的保证数据的持续性在外界有变化的情况下。所以,尽管Serializable效率低点,但 在数据存储在磁盘 或网络传输中,使用Serializable 最优 。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值