序列化-Serializable和Parcelable的简单介绍

序列化的本质

序列化是一种用来处理对象流的机制。序列化是为了解决在对对象流进行读写操作时所引发的问题。

序列化:将java对象转换成字节序列的过程,字节码可以保存到数据库、内存、文件等,也可用于网络传输

反序列化:将字节序列恢复为java对象的过程。

序列化实现的方式有很多方案,在java中是使用的JDK内置的Serializable接口来实现序列化,而Android SDK中增加Parcelable方式来实现序列化,除了常见的这2种还有很多其他优秀的序列化和反序列化方案(Twitter的Serial、Google的Protocol Buffers和flatbuffers等),这里先了解一下Serializable和Parcelable2种方式的原理和区别。

Serializable

Serializable是一个空的标记接口,没有任何方法和属性,implement Serializable只用于标记该对象是可以序列化的。如果一个类implement Serializable,则子类也是可以序列化的。

/**
 * Serializability of a class is enabled by the class implementing the
 * java.io.Serializable interface. Classes that do not implement this
 * interface will not have any of their state serialized or
 * deserialized.  All subtypes of a serializable class are themselves
 * serializable.  The serialization interface has no methods or fields
 * and serves only to identify the semantics of being serializable. 

* * To allow subtypes of non-serializable classes to be serialized, the * subtype may assume responsibility for saving and restoring the * state of the supertype's public, protected, and (if accessible) * package fields. The subtype may assume this responsibility only if * the class it extends has an accessible no-arg constructor to * initialize the class's state. It is an error to declare a class * Serializable if this is not the case. The error will be detected at * runtime.

* * During deserialization, the fields of non-serializable classes will * be initialized using the public or protected no-arg constructor of * the class. A no-arg constructor must be accessible to the subclass * that is serializable. The fields of serializable subclasses will * be restored from the stream.

* * When traversing a graph, an object may be encountered that does not * support the Serializable interface. In this case the * NotSerializableException will be thrown and will identify the class * of the non-serializable object.

* * Classes that require special handling during the serialization and * deserialization process must implement special methods with these exact * signatures: * *

 * private void writeObject(java.io.ObjectOutputStream out)
 *     throws IOException
 * private void readObject(java.io.ObjectInputStream in)
 *     throws IOException, ClassNotFoundException;
 * private void readObjectNoData()
 *     throws ObjectStreamException;
 * 
* *

The writeObject method is responsible for writing the state of the * object for its particular class so that the corresponding * readObject method can restore it. The default mechanism for saving * the Object's fields can be invoked by calling * out.defaultWriteObject. The method does not need to concern * itself with the state belonging to its superclasses or subclasses. * State is saved by writing the individual fields to the * ObjectOutputStream using the writeObject method or by using the * methods for primitive data types supported by DataOutput. * *

The readObject method is responsible for reading from the stream and * restoring the classes fields. It may call in.defaultReadObject to invoke * the default mechanism for restoring the object's non-static and * non-transient fields. The defaultReadObject method uses information in * the stream to assign the fields of the object saved in the stream with the * correspondingly named fields in the current object. This handles the case * when the class has evolved to add new fields. The method does not need to * concern itself with the state belonging to its superclasses or subclasses. * State is saved by writing the individual fields to the * ObjectOutputStream using the writeObject method or by using the * methods for primitive data types supported by DataOutput. * *

The readObjectNoData method is responsible for initializing the state of * the object for its particular class in the event that the serialization * stream does not list the given class as a superclass of the object being * deserialized. This may occur in cases where the receiving party uses a * different version of the deserialized instance's class than the sending * party, and the receiver's version extends classes that are not extended by * the sender's version. This may also occur if the serialization stream has * been tampered; hence, readObjectNoData is useful for initializing * deserialized objects properly despite a "hostile" or incomplete source * stream. * *

Serializable classes that need to designate an alternative object to be * used when writing an object to the stream should implement this * special method with the exact signature: * *

 * ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
 * 

* * This writeReplace method is invoked by serialization if the method * exists and it would be accessible from a method defined within the * class of the object being serialized. Thus, the method can have private, * protected and package-private access. Subclass access to this method * follows java accessibility rules.

* * Classes that need to designate a replacement when an instance of it * is read from the stream should implement this special method with the * exact signature. * *

 * ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
 * 

* * This readResolve method follows the same invocation rules and * accessibility rules as writeReplace.

* * The serialization runtime associates with each serializable class a version * number, called a serialVersionUID, which is used during deserialization to * verify that the sender and receiver of a serialized object have loaded * classes for that object that are compatible with respect to serialization. * If the receiver has loaded a class for the object that has a different * serialVersionUID than that of the corresponding sender's class, then * deserialization will result in an {@link InvalidClassException}. A * serializable class can declare its own serialVersionUID explicitly by * declaring a field named "serialVersionUID" that must be static, * final, and of type long: * *

 * ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
 * 
* * If a serializable class does not explicitly declare a serialVersionUID, then * the serialization runtime will calculate a default serialVersionUID value * for that class based on various aspects of the class, as described in the * Java(TM) Object Serialization Specification. However, it is strongly * recommended that all serializable classes explicitly declare * serialVersionUID values, since the default serialVersionUID computation is * highly sensitive to class details that may vary depending on compiler * implementations, and can thus result in unexpected * InvalidClassExceptions during deserialization. Therefore, to * guarantee a consistent serialVersionUID value across different java compiler * implementations, a serializable class must declare an explicit * serialVersionUID value. It is also strongly advised that explicit * serialVersionUID declarations use the private modifier where * possible, since such declarations apply only to the immediately declaring * class--serialVersionUID fields are not useful as inherited members. Array * classes cannot declare an explicit serialVersionUID, so they always have * the default computed value, but the requirement for matching * serialVersionUID values is waived for array classes. * * Android implementation of serialVersionUID computation will change slightly * for some classes if you're targeting android N. In order to preserve compatibility, * this change is only enabled is the application target SDK version is set to * 24 or higher. It is highly recommended to use an explicit serialVersionUID * field to avoid compatibility issues. * *

Implement Serializable Judiciously

* Refer to Effective Java's chapter on serialization for thorough * coverage of the serialization API. The book explains how to use this * interface without harming your application's maintainability. * *

Recommended Alternatives

* JSON is concise, human-readable and efficient. Android * includes both a {@link android.util.JsonReader streaming API} and a {@link * org.json.JSONObject tree API} to read and write JSON. Use a binding library * like GSON to read and * write Java objects directly. * * @author unascribed * @see java.io.ObjectOutputStream * @see java.io.ObjectInputStream * @see java.io.ObjectOutput * @see java.io.ObjectInput * @see java.io.Externalizable * @since JDK1.1 */
public interface Serializable { }

上面就是Serializable接口,可以看到注释特别长,总结一下大致如下:

  1. Serializable接口没有方法和属性字段,用于标记类可以序列化。
  2. 父类可序列化,则子类也可序列化。
  3. static或用transient关键字标记的字段不会被序列化
  4. Serializable内部是使用的ObjectOutputStream和ObjectInputStream来序列化和反序列化的,序列化时通过ObjectOutputStream的writeObject写入对象序列化流数据及状态,默认的保存机制是调用out.defaultWriteObject;反序列化时使用ObjectInputStream.readObject方法将流数据还原成类对象。
  5. 序列化运行时是通过serialVersionUID来判断版本的一致性,我们可以在要序列化的类中显式的声明一个static final long serialVerisonUID值,默认情况下java编译器会自动给我们生成一个serialVersionUID,但由于不同的java编译器可能生成的serialVersionUID不同,反序列化期间可能导致InvalidClassException,所以强烈建议我们自己定义serialVersionUID值。
  6. Serializable在使用是会产生大量临时变量,频繁GC,使用了反射,序列化过程较慢,所以官方推荐使用简洁高效的JSON代替Serializable。

序列化过程

下面看看ObjectOutputStream的writeObject相关的源码,简单看看序列化的过程

/**
 * 外部调用,序列化入口
 */
public final void writeObject(Object obj) throws IOException {
	...
	writeObject0(obj, false);
	...
}

/**
 * 写数据,unshared=false
 */
private void writeObject0(Object obj, boolean unshared) throws IOException {
    ...
    //判断序列化对象的类型
    if (obj instanceof Class) {
        writeClass((Class) obj, unshared);
    } else if (obj instanceof ObjectStreamClass) {
        writeClassDesc((ObjectStreamClass) obj, unshared);
    // END Android-changed:  Make Class and ObjectStreamClass replaceable.
    } else if (obj instanceof String) {
        writeString((String) obj, unshared);
    } else if (cl.isArray()) {
        writeArray(obj, desc, unshared);
    } else if (obj instanceof Enum) {
        writeEnum((Enum<?>) obj, desc, unshared);
    } else if (obj instanceof Serializable) {
      	//如果是可序列化的,开始写入数据
        writeOrdinaryObject(obj, desc, unshared);
    } else {
      	//不可序列化,抛出异常
        if (extendedDebugInfo) {
            throw new NotSerializableException(
                cl.getName() + "\n" + debugInfoStack.toString());
        } else {
            throw new NotSerializableException(cl.getName());
        }
    }
    ...
}

/**
 * 写object数据
 */
private void writeOrdinaryObject(Object obj, ObjectStreamClass desc, boolean unshared) throws IOException {
    if (extendedDebugInfo) {
        debugInfoStack.push(
            (depth == 1 ? "root " : "") + "object (class \"" +
            obj.getClass().getName() + "\", " + obj.toString() + ")");
    }
    try {
        desc.checkSerialize();
      	//写入标记类型,读的时候根据这个来判断
        bout.writeByte(TC_OBJECT);
      	//写入类的描述信息
        writeClassDesc(desc, false);
        handles.assign(unshared ? null : obj);
        if (desc.isExternalizable() && !desc.isProxy()) {
          	//如果是实现了Externalizable接口,则写入ExternalData(
            writeExternalData((Externalizable) obj);
        } else {
          	//写序列化数据
            writeSerialData(obj, desc);
        }
    } finally {
        if (extendedDebugInfo) {
            debugInfoStack.pop();
        }
    }
}

/**
 * 真正写object data的入口
 */
private void writeSerialData(Object obj, ObjectStreamClass desc)
        throws IOException {
    ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
  	//遍历父类和自己,for循环从父类开始写入数据
    for (int i = 0; i < slots.length; i++) {
        ObjectStreamClass slotDesc = slots[i].desc;
        if (slotDesc.hasWriteObjectMethod()) {
            PutFieldImpl oldPut = curPut;
            curPut = null;
            SerialCallbackContext oldContext = curContext;

            if (extendedDebugInfo) {
                debugInfoStack.push(
                    "custom writeObject data (class \"" +
                    slotDesc.getName() + "\")");
            }
            try {
                curContext = new SerialCallbackContext(obj, slotDesc);
                bout.setBlockDataMode(true);
                slotDesc.invokeWriteObject(obj, this);
                bout.setBlockDataMode(false);
                bout.writeByte(TC_ENDBLOCKDATA);
            } finally {
                curContext.setUsed();
                curContext = oldContext;
                if (extendedDebugInfo) {
                    debugInfoStack.pop();
                }
            }

            curPut = oldPut;
        } else {
          	//写入字段属性值
            defaultWriteFields(obj, slotDesc);
        }
    }
}

/**
 * 写基本数据类型数据
 */
private void defaultWriteFields(Object obj, ObjectStreamClass desc)
        throws IOException {
    Class<?> cl = desc.forClass();
    if (cl != null && obj != null && !cl.isInstance(obj)) {
        throw new ClassCastException();
    }

    desc.checkDefaultSerialize();

    int primDataSize = desc.getPrimDataSize();
    if (primVals == null || primVals.length < primDataSize) {
        primVals = new byte[primDataSize];
    }
    desc.getPrimFieldValues(obj, primVals);
    bout.write(primVals, 0, primDataSize, false);

    ObjectStreamField[] fields = desc.getFields(false);
    Object[] objVals = new Object[desc.getNumObjFields()];
    int numPrimFields = fields.length - objVals.length;
    desc.getObjFieldValues(obj, objVals);
    for (int i = 0; i < objVals.length; i++) {
        if (extendedDebugInfo) {
            debugInfoStack.push(
                "field (class \"" + desc.getName() + "\", name: \"" +
                fields[numPrimFields + i].getName() + "\", type: \"" +
                fields[numPrimFields + i].getType() + "\")");
        }
        try {
            writeObject0(objVals[i],
                         fields[numPrimFields + i].isUnshared());
        } finally {
            if (extendedDebugInfo) {
                debugInfoStack.pop();
            }
        }
    }
}

反序列化过程

再看看ObjectInputStream的readObject相关的源码,反序列化的过程

public final Object readObject() throws IOException, ClassNotFoundException {
    ...
    Object obj = readObject0(false);
    ...
}

/**
 * 读object数据
 */
private Object readObject0(boolean unshared) throws IOException {
    ...
    byte tc;
    while ((tc = bin.peekByte()) == TC_RESET) {
        bin.readByte();
        handleReset();
    }

    depth++;
    // Android-removed: ObjectInputFilter logic, to be reconsidered. http://b/110252929
    // totalObjectRefs++;
    try {
        switch (tc) {
            case TC_NULL:
                return readNull();

            case TC_REFERENCE:
                return readHandle(unshared);

            case TC_CLASS:
                return readClass(unshared);
            
            case TC_CLASSDESC:
            case TC_PROXYCLASSDESC:
                return readClassDesc(unshared);

            case TC_STRING:
            case TC_LONGSTRING:
                return checkResolve(readString(unshared));

            case TC_ARRAY:
                return checkResolve(readArray(unshared));

            case TC_ENUM:
                return checkResolve(readEnum(unshared));
            
            //判断类型如果是object类型,则读取object对象流
            case TC_OBJECT:
                return checkResolve(readOrdinaryObject(unshared));

            case TC_EXCEPTION:
                IOException ex = readFatalException();
                throw new WriteAbortedException("writing aborted", ex);

            case TC_BLOCKDATA:
            case TC_BLOCKDATALONG:
                if (oldMode) {
                    bin.setBlockDataMode(true);
                    bin.peek();             // force header read
                    throw new OptionalDataException(
                        bin.currentBlockRemaining());
                } else {
                    throw new StreamCorruptedException(
                        "unexpected block data");
                }

            case TC_ENDBLOCKDATA:
                if (oldMode) {
                    throw new OptionalDataException(true);
                } else {
                    throw new StreamCorruptedException(
                        "unexpected end of block data");
                }

            default:
                throw new StreamCorruptedException(
                    String.format("invalid type code: %02X", tc));
        }
    } finally {
        depth--;
        bin.setBlockDataMode(oldMode);
    }
}

/**
 * 读取流数据
 */
private Object readOrdinaryObject(boolean unshared) throws IOException {
    if (bin.readByte() != TC_OBJECT) {
        throw new InternalError();
    }

    ObjectStreamClass desc = readClassDesc(false);
    desc.checkDeserialize();

    Class<?> cl = desc.forClass();
    if (cl == String.class || cl == Class.class
            || cl == ObjectStreamClass.class) {
        throw new InvalidClassException("invalid class descriptor");
    }

    Object obj;
    try {
        obj = desc.isInstantiable() ? desc.newInstance() : null;
    } catch (Exception ex) {
        throw (IOException) new InvalidClassException(
            desc.forClass().getName(),
            "unable to create instance").initCause(ex);
    }

    passHandle = handles.assign(unshared ? unsharedMarker : obj);
    ClassNotFoundException resolveEx = desc.getResolveException();
    if (resolveEx != null) {
        handles.markException(passHandle, resolveEx);
    }

    if (desc.isExternalizable()) {
      	//读取自定义序列化数据
        readExternalData((Externalizable) obj, desc);
    } else {
      	//读取序列化数据
        readSerialData(obj, desc);
    }
    ...
    return obj;
}

/**
 * 读取序列化数据
 */
private void readSerialData(Object obj, ObjectStreamClass desc) throws IOException {
    ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
    for (int i = 0; i < slots.length; i++) {
        ObjectStreamClass slotDesc = slots[i].desc;

        if (slots[i].hasData) {
            if (obj == null || handles.lookupException(passHandle) != null) {
                defaultReadFields(null, slotDesc); // skip field values
            } else if (slotDesc.hasReadObjectMethod()) {
                // BEGIN Android-changed: ThreadDeath cannot cause corruption on Android.
                // Android does not support Thread.stop() or Thread.stop(Throwable) so this
                // does not need to protect against state corruption that can occur when a
                // ThreadDeath Error is thrown in the middle of the finally block.
                SerialCallbackContext oldContext = curContext;
                if (oldContext != null)
                    oldContext.check();
                try {
                    curContext = new SerialCallbackContext(obj, slotDesc);

                    bin.setBlockDataMode(true);
                    slotDesc.invokeReadObject(obj, this);
                } catch (ClassNotFoundException ex) {
                    /*
                     * In most cases, the handle table has already
                     * propagated a CNFException to passHandle at this
                     * point; this mark call is included to address cases
                     * where the custom readObject method has cons'ed and
                     * thrown a new CNFException of its own.
                     */
                    handles.markException(passHandle, ex);
                } finally {
                    curContext.setUsed();
                    if (oldContext!= null)
                        oldContext.check();
                    curContext = oldContext;
                    // END Android-changed: ThreadDeath cannot cause corruption on Android.
                }

                /*
                 * defaultDataEnd may have been set indirectly by custom
                 * readObject() method when calling defaultReadObject() or
                 * readFields(); clear it to restore normal read behavior.
                 */
                defaultDataEnd = false;
            } else {
                defaultReadFields(obj, slotDesc);
            }

            if (slotDesc.hasWriteObjectData()) {
                skipCustomData();
            } else {
                bin.setBlockDataMode(false);
            }
        } else {
            if (obj != null &&
                slotDesc.hasReadObjectNoDataMethod() &&
                handles.lookupException(passHandle) == null)
            {
                slotDesc.invokeReadObjectNoData(obj);
            }
        }
    }
}

/**
 * 读取字段属性数据
 */
private void defaultReadFields(Object obj, ObjectStreamClass desc) throws IOException {
    Class<?> cl = desc.forClass();
    if (cl != null && obj != null && !cl.isInstance(obj)) {
        throw new ClassCastException();
    }

    int primDataSize = desc.getPrimDataSize();
    if (primVals == null || primVals.length < primDataSize) {
        primVals = new byte[primDataSize];
    }
        bin.readFully(primVals, 0, primDataSize, false);
    if (obj != null) {
        desc.setPrimFieldValues(obj, primVals);
    }

    int objHandle = passHandle;
    ObjectStreamField[] fields = desc.getFields(false);
    Object[] objVals = new Object[desc.getNumObjFields()];
    int numPrimFields = fields.length - objVals.length;
    for (int i = 0; i < objVals.length; i++) {
        ObjectStreamField f = fields[numPrimFields + i];
        objVals[i] = readObject0(f.isUnshared());
        if (f.getField() != null) {
            handles.markDependency(objHandle, passHandle);
        }
    }
    if (obj != null) {
        desc.setObjFieldValues(obj, objVals);
    }
    passHandle = objHandle;
}

可以看到ObjectInputStream反序列化和ObjectOutputStream方法是一一对应,怎么写就怎么读取,其实java io API内部设计都是这样,所有的Input和Output,Reader和Writer方法基本都是一一对应,掌握了输出流就知道输入流对应该怎么写。

Externalizable

从上面的源码中可以看到有一个这个判断:

//ObjectOutputStream
if (desc.isExternalizable() && !desc.isProxy()) {
	writeExternalData((Externalizable) obj);
} 

//ObjectInputStream
if (desc.isExternalizable()) {
	readExternalData((Externalizable) obj, desc);
}

Serializable有一个直接子类Externalizable接口,该接口有两个方法writeExternal(ObjectOutput out)readExternal(ObjectInput in),可以使用该接口来实现自定义序列化细节。看下源码


/**
 * Only the identity of the class of an Externalizable instance is
 * written in the serialization stream and it is the responsibility
 * of the class to save and restore the contents of its instances.
 *
 * The writeExternal and readExternal methods of the Externalizable
 * interface are implemented by a class to give the class complete
 * control over the format and contents of the stream for an object
 * and its supertypes. These methods must explicitly
 * coordinate with the supertype to save its state. These methods supersede
 * customized implementations of writeObject and readObject methods.
* * Object Serialization uses the Serializable and Externalizable * interfaces. Object persistence mechanisms can use them as well. Each * object to be stored is tested for the Externalizable interface. If * the object supports Externalizable, the writeExternal method is called. If the * object does not support Externalizable and does implement * Serializable, the object is saved using * ObjectOutputStream.
When an Externalizable object is * reconstructed, an instance is created using the public no-arg * constructor, then the readExternal method called. Serializable * objects are restored by reading them from an ObjectInputStream.
* * An Externalizable instance can designate a substitution object via * the writeReplace and readResolve methods documented in the Serializable * interface.
* * @author unascribed * @see java.io.ObjectOutputStream * @see java.io.ObjectInputStream * @see java.io.ObjectOutput * @see java.io.ObjectInput * @see java.io.Serializable * @since JDK1.1 */
public interface Externalizable extends java.io.Serializable { /** * The object implements the writeExternal method to save its contents * by calling the methods of DataOutput for its primitive values or * calling the writeObject method of ObjectOutput for objects, strings, * and arrays. * * @serialData Overriding methods should use this tag to describe * the data layout of this Externalizable object. * List the sequence of element types and, if possible, * relate the element to a public/protected field and/or * method of this Externalizable class. * * @param out the stream to write the object to * @exception IOException Includes any I/O exceptions that may occur */ void writeExternal(ObjectOutput out) throws IOException; /** * The object implements the readExternal method to restore its * contents by calling the methods of DataInput for primitive * types and readObject for objects, strings and arrays. The * readExternal method must read the values in the same sequence * and with the same types as were written by writeExternal. * * @param in the stream to read data from in order to restore the object * @exception IOException if I/O errors occur * @exception ClassNotFoundException If the class for an object being * restored cannot be found. */ void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }

看一个示例,android.telephony.mbms.DownloadRequest中的SerializationDataContainer内部类实现了Externalizable接口来自定义序列化内容,代码如下。

private static class SerializationDataContainer implements Externalizable {
    private String fileServiceId;
    private Uri source;
    private Uri destination;
    private int subscriptionId;
    private String appIntent;
    private int version;

    public SerializationDataContainer() {}

    SerializationDataContainer(DownloadRequest request) {
        fileServiceId = request.fileServiceId;
        source = request.sourceUri;
        destination = request.destinationUri;
        subscriptionId = request.subscriptionId;
        appIntent = request.serializedResultIntentForApp;
        version = request.version;
    }

    @Override
    public void writeExternal(ObjectOutput objectOutput) throws IOException {
        objectOutput.write(version);
        objectOutput.writeUTF(fileServiceId);
        objectOutput.writeUTF(source.toString());
        objectOutput.writeUTF(destination.toString());
        objectOutput.write(subscriptionId);
        objectOutput.writeUTF(appIntent);
    }

    @Override
    public void readExternal(ObjectInput objectInput) throws IOException {
        version = objectInput.read();
        fileServiceId = objectInput.readUTF();
        source = Uri.parse(objectInput.readUTF());
        destination = Uri.parse(objectInput.readUTF());
        subscriptionId = objectInput.read();
        appIntent = objectInput.readUTF();
        // Do version checks here -- future versions may have other fields.
    }
}

//写入序列化数据
public byte[] toByteArray() {
    try {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
        SerializationDataContainer container = new SerializationDataContainer(this);
        stream.writeObject(container);
        stream.flush();
        return byteArrayOutputStream.toByteArray();
    } catch (IOException e) {
        // Really should never happen
        Log.e(LOG_TAG, "Got IOException trying to serialize opaque data");
        return null;
    }
}

//还原序列化对象
public static Builder fromSerializedRequest(byte[] data) {
    Builder builder;
    try {
        ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
        SerializationDataContainer dataContainer =
                (SerializationDataContainer) stream.readObject();
        builder = new Builder(dataContainer.source, dataContainer.destination);
        builder.version = dataContainer.version;
        builder.appIntent = dataContainer.appIntent;
        builder.fileServiceId = dataContainer.fileServiceId;
        builder.subscriptionId = dataContainer.subscriptionId;
    } catch (IOException e) {
        // Really should never happen
        Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
        throw new IllegalArgumentException(e);
    } catch (ClassNotFoundException e) {
        Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
        throw new IllegalArgumentException(e);
    }
    return builder;
}

到此Serializable了解的差不多了,下面看下Android中的Parcelable实现序列化的一些细节。

Parcelable

Parcelable是Android中的一种序列化机制,Parcelable的原理是通过Parcel将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,Parcel机制是将序列化的数据写入到一个共享内存当中,其他进程可以通过Parcel从这块共享内存中读出字节流,因此可以实现在Binder中跨进程传输数据,实现传递对象的功能。

看下Parcelable接口的源码

package android.os;

import android.annotation.IntDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Interface for classes whose instances can be written to
 * and restored from a {@link Parcel}.  Classes implementing the Parcelable
 * interface must also have a non-null static field called CREATOR
 * of a type that implements the {@link Parcelable.Creator} interface.
 * 
 * 

A typical implementation of Parcelable is:

* *
 * 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();
 *     }
 * }
*/
public interface Parcelable { /** @hide */ @IntDef(flag = true, prefix = { "PARCELABLE_" }, value = { PARCELABLE_WRITE_RETURN_VALUE, PARCELABLE_ELIDE_DUPLICATES, }) @Retention(RetentionPolicy.SOURCE) public @interface WriteFlags {} /** * Flag for use with {@link #writeToParcel}: the object being written * is a return value, that is the result of a function such as * "Parcelable someFunction()", * "void someFunction(out Parcelable)", or * "void someFunction(inout Parcelable)". Some implementations * may want to release resources at this point. */ public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001; /** * Flag for use with {@link #writeToParcel}: a parent object will take * care of managing duplicate state/data that is nominally replicated * across its inner data members. This flag instructs the inner data * types to omit that data during marshaling. Exact behavior may vary * on a case by case basis. * @hide */ public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002; /* * Bit masks for use with {@link #describeContents}: each bit represents a * kind of object considered to have potential special significance when * marshalled. */ /** @hide */ @IntDef(flag = true, prefix = { "CONTENTS_" }, value = { CONTENTS_FILE_DESCRIPTOR, }) @Retention(RetentionPolicy.SOURCE) public @interface ContentsFlags {} /** * Descriptor bit used with {@link #describeContents()}: indicates that * the Parcelable object's flattened representation includes a file descriptor. * * @see #describeContents() */ public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001; /** * Describe the kinds of special objects contained in this Parcelable * instance's marshaled representation. For example, if the object will * include a file descriptor in the output of {@link #writeToParcel(Parcel, int)}, * the return value of this method must include the * {@link #CONTENTS_FILE_DESCRIPTOR} bit. * * @return a bitmask indicating the set of special object types marshaled * by this Parcelable object instance. */ public @ContentsFlags 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, @WriteFlags 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接口实现序列化需要:

  1. 创建一个实现了Parcelable.Creator接口的静态常量,其中createFromParcel方法用于反序列化时创建新的实例。

  2. 重写writeToParcel(Parcel out, int flags) 方法通过Parcel将数据写入共享内存,parcel可以写入原始数据类型(如:writeInt()、writeFloat()、writeString()等)、也可以写入一个Parcelable对象writeParcelable(Parcelable p, int parcelableFlags)、还提供了2种写入序列化对象集合的方式writeList(List)writeTypedList(List)

  3. 声明一个Parcel参数的构造方法读取Parcel数据

Parcel中的写和读都是native方法,底层采用c语言编写,具体的源码就不去研究了。

在内存上序列化的时候,Parcelable比Serializable性能高,所以尽量使用Parcelable来序列化;但是Parcelable不适合磁盘上的数据持久化,所以这种情况使用Serializable更好。

你可能感兴趣的:(Java,java)