文章出处:https://justinwei.blog.youkuaiyun.com/article/details/47122687
请转载的朋友标明出处~~
0. 前言
之前一直在用这两个类,但是没有细细的研究,偶尔在网上看到了相关的解释,感觉不是很详细,这里小结一下,如果还有什么遗漏希望有朋友能提出来。
1. parcel
首先说一下parcel,因为 parcelable 核心也是依赖 parcel 的。
java中serialize机制,是能将数据对象存入字节流中,在需要的时候重新生成对象,主要应用是利用外部存储设备保存对象状态,以及通过网络传输对象等。
在Android系统中parcel设计主要是在IPC中通信,将对象存储在内存中,这样效率更高,定位为轻量级的高效的对象序列化和反序列化机制。
(以下路径是在android 4.4中,其他版本的路径可能有点变化,但是应该不大,Parcel毕竟是公共中的公共模块。)
frameworks/base/core/java/android/os/Parcel.java
public final class Parcel {// final class
......
/**
* Retrieve a new Parcel object from the pool.
*/
public static Parcel obtain() {
final Parcel[] pool = sOwnedPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
return p;
}
}
}
return new Parcel(0);
}
......
/**
* 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;
freeBuffer();
final Parcel[] pool;
if (mOwnsNativeParcelObject) {
pool = sOwnedPool;
} else {
mNativePtr = 0;
pool = sHolderPool;
}
synchronized (pool) {
for (int i=0; i<POOL_SIZE; i++) {
if (pool[i] == null) {
pool[i] = this;
return;
}
}
}
}
......
/**
* Returns the total amount of data contained in the parcel.
*/
public final int dataSize() {
return nativeDataSize(mNativePtr);
}
/**
* Returns the amount of data remaining to be read from the
* parcel. That is, {@link #dataSize}-{@link #dataPosition}.
*/
public final int dataAvail() {
return nativeDataAvail(mNativePtr);
}
/**
* Returns the current position in the parcel data. Never
* more than {@link #dataSize}.
*/
public final int dataPosition() {
return nativeDataPosition(mNativePtr);
}
......
/**
* Write an integer value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
public final void writeInt(int val) {
nativeWriteInt(mNativePtr, val);
}
/**
* Write a long integer value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
public final void writeLong(long val) {
nativeWriteLong(mNativePtr, val);
}
......
}
详细的code就不贴出来了,可以查看source code。
- obtain()
获取一个新的Parcel对象
- recycle()
清空、回收Parcel对象的内存
- dataSize()
返回Parcel中所有数据在用的内存
- dataAvail()
返回Parcel剩下没有读取的data的空间,dataSize - dataPosition
- dataPosition()
获取Parcel对象数据的偏移
- dataCapacity()
返回Parcel对象占用的总的内存大小,这个区别于dataSize是所有数据用的内存,还有多余的内存用于re-allocate
- setDataSize(int)
字节为单位
- setDataPosition(int)
0 ~ dataSize()
- setDataCapacity(int)
字节为单位
- writeInt(int)
- writeLong(long)
- writeFloat(float)
- writeDouble(double)
- writeString(string)
- writeCharSequence(CharSequence)
- writeByte(byte)
- writeStrongBinder(IBinder)
- writeStrongInterface(IInterface)
- writeFileDescriptor(FileDescriptor)
对应的是read**
- writeValue(Object)
- readValue(ClassLoader)
- Parcel(int nativePtr)
1.1 构造函数
frameworks/base/core/java/android/os/Parcel.java
private Parcel(int nativePtr) {
if (DEBUG_RECYCLE) {
mStack = new RuntimeException();
}
//Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
init(nativePtr);
}
private void init(int nativePtr) {
if (nativePtr != 0) {
mNativePtr = nativePtr;
mOwnsNativeParcelObject = false;
} else {
mNativePtr = nativeCreate();
mOwnsNativeParcelObject = true;
}
}
不管是wirteInt 还是什么都有 mNativePtr,就是这里来的,刚新建的Parcel应该是需要nativeCreate() 的。
上面的接口都是native的接口,来看一下(太多了,找writeInt为例吧):
frameworks/base/core/jni/android_os_Parcel.cpp
{"nativeWriteInt", "(II)V", (void*)android_os_Parcel_writeInt},
frameworks/base/core/jni/android_os_Parcel.cpp
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jint nativePtr, jint val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
const status_t err = parcel->writeInt32(val);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
需要调用 Parcel 类的writeInt32():
frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
template<class T>
status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
*reinterpret_cast<T*>(mData+mDataPos) = val;
return finishWrite(sizeof(val));
}
status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}
- PAD_SIZE
#define PAD_SIZE(s) (((s)+3)&~3)
读写时4个字节对齐
- mDataPos+sizeof(val)) <= mDataCapacity
当小于Parcel的总的大小时候,是可以直接write的。
但是如果不小,那么就需要重新计算大小,然后write
status_t Parcel::growData(size_t len)
{
size_t newSize = ((mDataSize+len)*3)/2;
return (newSize <= mDataSize)
? (status_t) NO_MEMORY
: continueWrite(newSize);
}
mDataSizeSize会多分配50%的大小
continueWrite就是对这些多分配的空间进行初始化、判断,malloc好后进行memcpy,这样的内存操作效率会比Java 序列化中使用外部存储器会高很多。
- finishWrite(size_t)
status_t Parcel::finishWrite(size_t len)
{
//printf("Finish write of %d\n", len);
mDataPos += len;
ALOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos);
if (mDataPos > mDataSize) {
mDataSize = mDataPos;
ALOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize);
}
//printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
return NO_ERROR;
}
Parcel中data的pos进行偏移,mDataPos记录了当前data最后一次存放的位置。
- *reinterpret_cast<T*>(mData+mDataPos) = val;
*(mData+mDataPos)指的就是Parcel中data区的指针,将val存进去就可以。
上面就是writeInt的整个过程了。
再来看一下readInt():
frameworks/native/libs/binder/Parcel.cpp
template<class T>
status_t Parcel::readAligned(T *pArg) const {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(T)) <= mDataSize) {
const void* data = mData+mDataPos;
mDataPos += sizeof(T);
*pArg = *reinterpret_cast<const T*>(data);
return NO_ERROR;
} else {
return NOT_ENOUGH_DATA;
}
}
对比writeInt() 和readInt() 你会发现,mDataPos 会一直往后,也就是说 write 的顺序是什么,read的顺序也必须是一样的。
2. Parcelable
之前讲过序列化的意义,在IPC间、网络间等传递对象,我们遇到的不但Intent中会用到,AIDL中也会用到。Parcelable接口相对Java 本身的Serializable稍微复杂了一点,但是效率高,c/c++中完全是对内存直接操作。
2.1 选择序列化方法的原则
1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。
2.2 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 static field called <code>CREATOR</code>, which
* is an object implementing the {@link Parcelable.Creator Parcelable.Creator}
* interface.
这里说是在用到Parcel进行一些存储的时候,需要用到这个Parcelable 接口。在implements这个Parcelable接口的时候,必须同时顶一个static的变量CREATOR,类型是Parcelable.Creator。
2.3 详细的Parcelable接口
public interface Parcelable {
/**
* Flag for use with {@link #writeToParcel}: the object being written
* is a return value, that is the result of a function such as
* "<code>Parcelable someFunction()</code>",
* "<code>void someFunction(out Parcelable)</code>", or
* "<code>void someFunction(inout Parcelable)</code>". Some implementations
* may want to release resources at this point.
*/
public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
/**
* Bit masks for use with {@link #describeContents}: each bit represents a
* kind of object considered to have potential special significance when
* marshalled.
*/
public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
/**
* Describe the kinds of special objects contained in this Parcelable's
* marshalled representation.
*
* @return a bitmask indicating the set of special object types marshalled
* by the Parcelable.
*/
public int describeContents();
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
public void writeToParcel(Parcel dest, int flags);
/**
* Interface that must be implemented and provided as a public CREATOR
* field that generates instances of your Parcelable class from a Parcel.
*/
public interface Creator<T> {
/**
* Create a new instance of the Parcelable class, instantiating it
* from the given Parcel whose data had previously been written by
* {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
*
* @param source The Parcel to read the object's data from.
* @return Returns a new instance of the Parcelable class.
*/
public T createFromParcel(Parcel source);
/**
* Create a new array of the Parcelable class.
*
* @param size Size of the array.
* @return Returns an array of the Parcelable class, with every entry
* initialized to null.
*/
public T[] newArray(int size);
}
/**
* Specialization of {@link Creator} that allows you to receive the
* ClassLoader the object is being created in.
*/
public interface ClassLoaderCreator<T> extends Creator<T> {
/**
* Create a new instance of the Parcelable class, instantiating it
* from the given Parcel whose data had previously been written by
* {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and
* using the given ClassLoader.
*
* @param source The Parcel to read the object's data from.
* @param loader The ClassLoader that this object is being created in.
* @return Returns a new instance of the Parcelable class.
*/
public T createFromParcel(Parcel source, ClassLoader loader);
}
}
- 变量 PARCELABLE_WRITE_RETURN_VALUE
writeToParcel() 相关操作的时候可能会用到的一个flag
- 变量 CONTENTS_FILE_DESCRIPTOR
marshall的时候用到,每个bit代表一个特殊的意义
- describeContents()
默认return 0 可以,具体做什么有待进一步研究
- writeToParcel(Parcel dest, int flags)
这个是需要实现的,第一个参数是需要 write 的Parcel,另一个参数是额外的 flag,0 或者之前的flag PARCELABLE_WRITE_RETURN_VALUE
- interface Creator<T>
这是必须要的,可以创建一个或者多个Parcelable实例,函数createFromParcel() 的参数Parcel就是之前 writeToParcel() 中的Parcel。
- public interface ClassLoaderCreator<T> extends Creator<T>
继承Creator,函数createFromParcel() 的第一个参数跟Creator是一样的,第二个参数带了ClassLoader,说明可以反序列化不同ClassLoader中的对象。
3. 实例
public class MyParcelable implements Parcelable
{
private int mData;
public int describeContents() //直接返回0
{
return 0;
}
public void writeToParcel(Parcel out, int flags) //需要存储的Parcel
{
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() //必须是public static final 类型
{
public MyParcelable createFromParcel(Parcel in) //读出之前writeToParcel中存储的Parcel,可以通过构造函数分别read出来,进行初始化
{
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size)
{
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in)//读出来进行初始化
{
mData = in.readInt();
}
}
请看code中的注释。