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)难以维护
至于选择哪种方式序列化,还需酌情选择。


最后,谢谢大家的观看!

更多相关文章

  1. 关于安装Android Studio的一些问题的解决方法
  2. android 按钮点击的两种方法以及长按事件
  3. Android 使用Handler的PostDelayed方法实现图片的轮播
  4. Android 应用启动闪白一下处理方法
  5. android 调用js中的方法

随机推荐

  1. adb使用wifi无线连接调试Android设备
  2. Android(安卓)图片添加水印
  3. HttpURLconnection上传数据
  4. Android(安卓)socket通信app Client端
  5. 调用getChildFragmentManager时出现的Bug
  6. Android编译环境搭建
  7. Android(安卓)socket通信app Server端
  8. MemoryCache
  9. 《Pro Android(安卓)Graphics》读书笔记
  10. bionic test :StringTestState