Parcelable和Serializable的区别

本文深入解析了Android中Parcelable接口的应用及其实现原理,对比了Parcelable与Serializable的区别,通过实例展示了Parcelable的具体使用方法,并探讨了其在Android应用开发中的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://www.tuicool.com/articles/MJzAZn

一、序列化、反序列化是什么?

(1) 名词解释

对象的序列化 : 把Java对象转换为字节序列并存储至一个储存媒介的过程。 
对象的反序列化:把字节序列恢复为Java对象的过程。 

(2) 序列化详细解释

对象的序列化涉及三个点关键点:Java对象、字节序列、存储。 
1. Java对象的组成? 
Java对象包含变量与方法。但是序列与反序列化仅处理Java变量而不处理方法,序列与反序列化仅对数据进行处理。 
2. 什么是字符序列? 
字符序列是两个词,字符是在计算机和电信领域中,字符(Character)是一个信息单位。数学上,序列是被排成一列的对象(或事件)。 
《字符-维基百科》   ,  《序列-维基百科》   说白了就是连续排列的多个字符的集合。类似于"1A165613246546" 
3. 存储 
字符序列需要保存到一个地方,可以是硬盘也可以是内存。 
简单说法是:序列化把当前对象信息保存下来。反序列化刚好相反的操作。 

二、Java对象与Java对象序列化的区别?

Java对象存在的前提必须在JVM运行期间存在,如果想在JVM非运行的情况下或者在其他机器JVM上获取指定Java对象,在现有Java对象的机制下都不可能完成。 
与Java对象不同的是,如果对Java对象执行序列化操作,因为原理是把Java对象信息保存到存储媒介,所以可以在以上Java对象不可能存在的两种情况下依然可以使用Java对象。 

三、为什么要使用序列化、反序列化?

根据以上对序列化、反序列化的理解,这个疑问可以翻译成,为什么需要把对象信息保存到存储媒介中并之后读取出来? 
因为二中的解释,开发中有在JVM非运行的情况下或者在其他机器JVM上获取指定Java对象的需求。 

四、Android 中Serializable与Parcelable区别?

两种都是用于支持序列化、反序列化话操作,两者最大的区别在于存储媒介的不同,Serializable使用IO读写存储在硬盘上,而Parcelable是直接在内存中读写,很明显内存的读写速度通常大于IO读写,所以在Android中通常优先选择Parcelable。 
Serializable不是当前关注的焦点,不过可以查看  《Java序列化算法透析》  这篇文章中实现一个简单的Serializable例子,查看序列化生成的IO文件,并且以16进制读取并一一解释每一个16进制数字的含义。 

五、Parcelable举例

在Android中实现Parcelable接口的类可以支持序列与反序列化,以下是一个实现的举例: 
1. 实现Parcelable接口 
2. 添加实体属性 
3. 覆写writeToParcel(Parcel dest, int flags)方法,指定写入Parcel类的数据。 
4. 创建Parcelable.Creator静态对象,有两个方法createFromParcel(Parcel in)与newArray(int size),前者指定如何从Parcel中读取出数据对象,后者创建一个数组。 
5. 覆写describeContents方法,默认返回0。 

public class Gril implements Parcelable {

	private int mAge; // 年龄
	private boolean mSexy; // 是否性感
    
	@Override
	public void writeToParcel(Parcel dest, int flags) {
		dest.writeInt(mAge);
		dest.writeByte((byte) (mSexy ? 1 : 0));
	}
    
	public static final Parcelable.Creator<Gril> CREATOR = new Parcelable.Creator<Gril>() {
		public Gril createFromParcel(Parcel in) {
			Gril gril = new Gril();
			gril.mAge = in.readInt();
			gril.mSexy = in.readByte() != 0;
		    return gril;
		}
	    
		public Gril[] newArray(int size) {
		    return new Gril[size];
		}
	};
    
	@Override
	public int describeContents() {
		return 0;
	}
}

六、Parcelable原理

    从上面的例子中可以看出,具体的写入(dest.writeInt(mAge);)与读取(gril.mAge = in.readInt();)都是针对Parcel对象进行的操作,下面贴出的是Parcle 读写int类型数据的定义。

public final class Parcel {

    ......
    
    /**
     * Write an integer value into the parcel at the current dataPosition(),
     * growing dataCapacity() if needed.
     */
    public final native void writeInt(int val);

    /**
     * Read an integer value from the parcel at the current dataPosition().
     */
    public final native int readInt();
    
     ......
}
从上面代码可以看出都是native方法说明都是使用JNI,其具体位置在system/frameworks/base/core/jni/android_util_Binder.cpp ,以下也仅以int类型读写为例 
static void android_os_Parcel_writeInt(JNIEnv* env, jobject clazz, jint val)
{
	Parcel* parcel = parcelForJavaObject(env, clazz);
	if (parcel != NULL) {
		const status_t err = parcel->writeInt32(val);
		if (err != NO_ERROR) {
			jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
		}
	}
}

static jint android_os_Parcel_readInt(JNIEnv* env, jobject clazz)
{
	Parcel* parcel = parcelForJavaObject(env, clazz);
	if (parcel != NULL) {
		return parcel->readInt32();
	}
	return 0;
}
从上面可以看出都会调用Parcel实现且分别调用writeInt32与readInt32函数,接着来看看具体实现。位置:/system/frameworks/base/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;
}


status_t Parcel::readInt32(int32_t *pArg) const
{
	return readAligned(pArg);
}

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;
	}
}
以下4点摘自  《探索Android中的Parcel机制(上)》  
有兴趣的朋友可以自己读一下,不难理解,这里把基本的思路总结一下: 
1. 整个读写全是在内存中进行,主要是通过malloc()、realloc()、memcpy()等内存操作进行,所以效率比JAVA序列化中使用外部存储器会高很多; 
2. 读写时是4字节对齐的,可以看到#define PAD_SIZE(s) (((s)+3)&~3)这句宏定义就是在做这件事情; 
3. 如果预分配的空间不够时newSize = ((mDataSize+len)*3)/2;会一次多分配50%; 

4. 对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不用重新new一个新对象。

七、序列化反序列化Parcelable实验?

1. 任何实体类都需要复写Parcelable接口吗? 
2. 如果子类新增属性,需要复写父类writeToParcel与CREATOR吗? 
3. writeToParcel 与 createFromParcel 对变量的读写前后顺序可以不一致吗,会出现什么结果? 
4. 读写Parcelable对象(写操作dest.writeParcelable(obj, flags);  读操作in.readParcelable(ObjectA.class.getClassLoader()); ) 
5. 读写Parcelable对象数组 
dest.writeParcelableArray(mClassNameList.toArray(new ClassName[mClassNameList.size()]), flags);

Parcelable[] parcelableArr = in.readParcelableArray(ClassName.class.getClassLoader());
ArrayList<ClassName> arrayList = new ArrayList<ClassName>();
for (Parcelable object : parcelableArr) {
     arrayList.add((ClassName)object);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值