android——Serializable & Parcelable

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)难以维护

至于选择哪种方式序列化,还需酌情选择。

最后,谢谢大家的观看!

时间: 2024-11-05 14:39:14

android——Serializable & Parcelable的相关文章

Android中Parcelable与Serializable接口用法

转自: Android中Parcelable接口用法 1. Parcelable接口 Interface for classes whose instances can be written to and restored from a Parcel. Classes implementing the Parcelable interface must also have a static field called CREATOR, which is an object implementing

Android高手进阶教程(十七)之---Android中Intent传递对象的两种方法(Serializable,Parcelable)!

[转][原文] 大家好,好久不见,今天要给大家讲一下Android中Intent中如何传递对象,就我目前所知道的有两种方法,一种是Bundle.putSerializable(Key,Object);另一种是Bundle.putParcelable(Key, Object);当然这些Object是有一定的条件的,前者是实现了Serializable接口,而后者是实现了Parcelable接口,为了让大家更容易理解我还是照常写了一个简单的Demo,大家就一步一步跟我来吧! 第一步:新建一个andr

Android中Intent传递对象的两种方法(Serializable,Parcelable)

今天要给大家讲一下Android中 Intent中如何传递对象,就我目前所知道的有两种方法,一种是Bundle.putSerializable(Key,Object);另一种是 Bundle.putParcelable(Key, Object);当然这些Object是有一定的条件的,前者是实现了Serializable接口,而后者是实现了Parcelable接口,为了让大 家更容易理解我还是照常写了一个简单的Demo,大家就一步一步跟我来吧! 第一步:新建一个Android工程命名为Object

(六十四)Android中Intent传递对象的两种方法(Serializable,Parcelable)

转载自:http://blog.csdn.net/android_tutor/article/details/5740845 大家好,好久不见,今天要给大家讲一下Android中Intent中如何传递对象,就我目前所知道的有两种方法,一种是Bundle.putSerializable(Key,Object);另一种是Bundle.putParcelable(Key, Object);当然这些Object是有一定的条件的,前者是实现了Serializable接口,而后者是实现了Parcelable

Android高手之路之Android中Intent传递对象的两种方法Serializable,Parcelable

注:本文改编自Android_Tutor的文章,原文地址:http://blog.csdn.net/android_tutor/article/details/5740845 Android中的传递有两个方法,一个是Serializable,另一个是Parcelable. Serializable是J2SE本身就支持的.而Parcelable是Android所特有的. 二者的使用场景和区别: 1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelab

[转]Android中Intent传递对象的两种方法(Serializable,Parcelable)

http://blog.csdn.net/xyz_lmn/article/details/5908355 今天要给大家讲一下Android中Intent中如何传递对象,就我目前所知道的有两种方法,一种是Bundle.putSerializable(Key,Object);另一种是Bundle.putParcelable(Key, Object);当然这些Object是有一定的条件的,前者是实现了Serializable接口,而后者是实现了Parcelable接口,为了让大家更容易理解我还是照常写

android中Parcelable接口的使用

android中Parcelable接口的使用 一.理解 Parcelable是一个接口.用来实现序列化.与此类似的还有一个接口Serializable,这是JavaSE本身支持的,而Parcelable是android特有的.二者比较: 1.Parcelable使用起来稍复杂点,而后者使用起来非常简单.下面例子中会看到. 2.Parcelable效率比Serializable高,支持Intent数据传递,也支持进程间通信(IPC). 3.Parcelable使用时要用到一个Parcel,可以简

android基础----&gt;Parcelable的使用

android中Parcelable序列化的使用. 目录导航: Serializable在android中的使用 Parcelable在android中的使用 Serializable与Parcelable的比较 友情链接 项目结构如下: Serializable在android中的使用 一. 建立一个实现了Serializable接口的Man类: package com.example.linux.parcelabletest; import java.io.Serializable; /**

Android中Parcelable序列化总结

在使用Parcelable对android中数据的序列化操作还是比较有用的,有人做过通过对比Serializable和Parcelable在android中序列化操作对象的速度比对,大概Parcelable相比Serializable要快10倍左右...给一个连接可以瞅瞅他们序列化的区别http://greenrobot.me/devpost/android-parcelable-serializable/ 下面来总结一下我们基本数据类型.对象.数组.list等做Parcelable的方法,主要