Serializable & Parcelable这两种序列化方法是Android中经常使用的方法,Serializable是Android从Java中继承过来的,Parcelable是Android自己提供的方法,Google是推荐使用Parcelable,至于这两种方法的区别,下面通过对源码的分析来慢慢的了解。
在分析源码之前,首先还是说一下序列化在Android中使用的场景:
1)我们在四大组件之间使用intente来传递数据,intente中所传递的数据是需要序列化的
2)binder,binder是IPC机制中是很重要的,binder中所传递的数据也是需要序列化的
3)还有我们经常要把一些数据持久化到存储设备上,这些数据是需要序列化的
4)在网络中传递对象
好啦,现在我们开始对两种序列化方式的源码进行了解:
Serializable:
因为Serializable是一个标示接口,所以我们主要是对它的注解进行阅读
* Marks classes that can be serialized by {@link ObjectOutputStream} and * deserialized by {@link ObjectInputStream}.
这一句告诉我们,serializable是通过ObjectOutputStream、ObjectInputStream来完成序列化过程的
* <p><strong>Warning:</strong> this interface limits how its implementing * classes can change in the future. By implementing {@code Serializable} you * expose your flexible in-memory implementation details as a rigid binary * representation. Simple code changes--like renaming private fields--are * not safe when the changed class is serializable.
这一句告诉我们,当实现Serializable接口的类中有代码变化,可能会导致序列化失败
<p>Every serializable class is assigned a version identifier called a {@code * serialVersionUID}. By default, this identifier is computed by hashing the * class declaration and its members. This identifier is included in the * serialized form so that version conflicts can be detected during * deserialization. If the local {@code serialVersionUID} differs from the * {@code serialVersionUID} in the serialized data, deserialization will fail * with an {@link InvalidClassException}. * * <p>You can avoid this failure by declaring an explicit {@code * serialVersionUID}. Declaring an explicit {@code serialVersionUID} tells the * serialization mechanism that the class is forward and backward compatible * with all versions that share that {@code serialVersionUID}. Declaring a * {@code serialVersionUID} looks like this: <pre> {@code * * private static final long serialVersionUID = 0L; * }</pre> * If you declare a {@code serialVersionUID}, you should increment it each * time your class changes incompatibly with the previous version. Typically * this is when you add, change or remove a non-transient field.
序列化和反序列化其实是通过serialVersionUID来进行工作的,默认情况下,serialVersionUID是通过hash计算类的成员变量和类的方法来得到的,serialVersionUID是存储在系列化的文件中的,当要反序列化时,就从序列化文件中能拿到serialVersionUID,如果拿到的serialVersionUiD与通过类中的成员变量和方法hash计算出的serialVersionUID一样的话,就反序列化成功,反序列化成功返回的结果与序列化的结果不是同一个对象,但是值是相同的,如果反序列化失败的话就会抛出一个
IncaildClassException。
当然我们也可以在类中自定义serialVersionUID,比如:
private static final long serialVersionUID = 0L;
这样的好处是,当类中的成员变量或者是方法增加、改变、移除的话,我们的反序列化依旧会成功,但是当类的结构有了翻天覆地的变化的时候,也会反序列化失败的。
上面有一句是:
Typically this is when you add, change or remove a non-transient field.
告诉我们,被transient修饰的成员变量是不会参与序列化的。
* <p>You can take control of your serialized form by implementing these two * methods with these exact signatures in your serializable classes: * <pre> {@code * * private void writeObject(java.io.ObjectOutputStream out) * throws IOException { * // write 'this' to 'out'... * } * * private void readObject(java.io.ObjectInputStream in) * throws IOException, ClassNotFoundException { * // populate the fields of 'this' from the data in 'in'... * } * }</pre>
这段注解告诉我们:默认的序列化过程和反序列化过程是可以改变的,只要重写wireteObject、readObject这两个方法就可以啦,但是一般的情况下我们是不会重写这两个方法的,下面就让我们看看默认它们是怎么实现的:
先看writeObject:
它是ObjectOutputStream中的方法:
public final void writeObject(Object object) throws IOException { writeObject(object, false); }
private void writeObject(Object object, boolean unshared) throws IOException { boolean setOutput = (primitiveTypes == output); if (setOutput) { primitiveTypes = null; } // This is the specified behavior in JDK 1.2. Very bizarre way to allow // behavior overriding. if (subclassOverridingImplementation && !unshared) { writeObjectOverride(object); return; } try { // First we need to flush primitive types if they were written drain(); // Actual work, and class-based replacement should be computed // if needed. writeObjectInternal(object, unshared, true, true); if (setOutput) { primitiveTypes = output; } } catch (IOException ioEx1) { // This will make it pass through until the top caller. Only the top caller writes the // exception (where it can). if (nestedLevels == 0) { try { writeNewException(ioEx1); } catch (IOException ioEx2) { // If writing the exception to the output stream causes another exception there // is no need to propagate the second exception or generate a third exception, // both of which might obscure details of the root cause. } } throw ioEx1; // and then we propagate the original exception } }
这段中会判断是否在子类中实现啦writeObject,如果实现了就直接返回,否则就会调用
writeObjectInternal(object, unshared, true, true);
private int writeObjectInternal(Object object, boolean unshared, boolean computeClassBasedReplacement, boolean computeStreamReplacement) throws IOException { ... f (!(enableReplace && computeStreamReplacement)) { // Is it a Class ? if (objClass == ObjectStreamClass.CLASSCLASS) { return writeNewClass((Class<?>) object, unshared); } // Is it an ObjectStreamClass ? if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) { return writeClassDesc((ObjectStreamClass) object, unshared); } } '''' }
这里面我只复制了一些代码,像writeNewClass、wirteClassDesc还有好多这种方法,他们的内部都是在output中存储类型,在objectWritten中存储对象。
再看readObject:
readObject是ObjectInoutStream中的方法:
public final Object readObject() throws OptionalDataException, ClassNotFoundException, IOException { return readObject(false); }
private Object readObject(boolean unshared) throws OptionalDataException, ClassNotFoundException, IOException { ... Object result; try { // We need this so we can tell when we are returning to the // original/outside caller if (++nestedLevels == 1) { // Remember the caller's class loader callerClassLoader = VMStack.getClosestUserClassLoader(bootstrapLoader, systemLoader); } result = readNonPrimitiveContent(unshared); if (restoreInput) { primitiveData = input; } } ... }
调用readNonPrimitiveContent();
private Object readNonPrimitiveContent(boolean unshared) throws ClassNotFoundException, IOException { checkReadPrimitiveTypes(); if (primitiveData.available() > 0) { OptionalDataException e = new OptionalDataException(); e.length = primitiveData.available(); throw e; } do { byte tc = nextTC(); switch (tc) { case TC_CLASS: return readNewClass(unshared); case TC_CLASSDESC: return readNewClassDesc(unshared); case TC_ARRAY: return readNewArray(unshared); case TC_OBJECT: return readNewObject(unshared); case TC_STRING: return readNewString(unshared); case TC_LONGSTRING: return readNewLongString(unshared); case TC_ENUM: return readEnum(unshared); case TC_REFERENCE: if (unshared) { readNewHandle(); throw new InvalidObjectException("Unshared read of back reference"); } return readCyclicReference(); case TC_NULL: return null; case TC_EXCEPTION: Exception exc = readException(); throw new WriteAbortedException("Read an exception", exc); case TC_RESET: resetState(); break; case TC_ENDBLOCKDATA: // Can occur reading class annotation pushbackTC(); OptionalDataException e = new OptionalDataException(); e.eof = true; throw e; default: throw corruptStream(tc); } // Only TC_RESET falls through } while (true); }
这里面就是在一个do—while循环里,通过nextTC()来获取输入流中的字段的类型,然后根据不同的类型得到不同的对象
private byte nextTC() throws IOException { if (hasPushbackTC) { hasPushbackTC = false; // We are consuming it } else { // Just in case a later call decides to really push it back, // we don't require the caller to pass it as parameter pushbackTC = input.readByte(); } return pushbackTC; }
这里input其实就是上面那个output,在do-while循环里就是不断的从input中取,然后获取字段,最终把字段封装成一个对象返回,这就是反序列化的过程。
可以看出在序列化中要进行大量的I\O的操作,而且里面还有用到反射的机制,对资源的消耗也比较大
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中必须要有一个成员变量,而且还要被 static field来修饰,类型为Parcelable.Creator,它是Parcelable中的一个内部接口
<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>
这段注解是Android给我们提供的一个例子,因为Parcelable相比于Serializable实现起来比较复杂,可能处于这种考虑,就给我们一个实例,确实给我们带来啦一些好处,我们就不用那么刻意的去记,当使用的时候看看源码就OK了!
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();
文件描述符,当对象有文件描述符时就返回1(CONTENTS_FILE_DESCRIPTOP),否则就返回0,大多数情况就返回0
/** * 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);
该方法是将对象写入到序列化结构中,dest:对象应该要被写入的序列化结构的容器
flags:有两种结果,为1(PARCELABLE_WRITE_RETURN_VALUE)从字面意 思可以看到就是要求要返回对象,大多数情况下是为0的。
/** * 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> { }
这是Parcelable中的内部接口,它的作用是从一个Parcel中构造出一个实现了Parcelable的类的实例,也就是承担了反序列化的过程。
然后看Creator中的两个接口:
/** * 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);
这个方法就是从Parcel中构造出一个实现了Parcelable的类的实例,一般我们实现是通过类的构造方法中实现的:
return new T(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);
创建一个指定长度的原始对象的数组,一般我们就直接返回:
return new T[size];
在Parcelable中实现序列化和反序列化主要是通过Parcel来实现的,对于Parcel,可以去看
Android中的Parcel机制(上)
最后进行一下总结:
优点 | 缺点 | |
Serializable | 1)实现起来简单
2)通过自定义serialVersionUID 可以 减少反序列化失败的发生 |
1)反射
2)大量的I\O操作,反序列化速度慢 2)耗资源 |
parcelable | 1)速度快 | 1)实现难,难以阅读
2)难以维护 |
至于选择哪种方式序列化,还需酌情选择。
最后,谢谢大家的观看!