8月更文挑战|Android基础开发-序列化方式Serializable和Parcelable

486 阅读7分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

开始

为什么需要序列化,因为Java进程间通讯数据不能直接传递,必需进行序列化传递在拿到序列化之后再反序列化拿到原本数据。而在Android开发中同一个应用程序中Intent传递的对象都需要序列化!其中有跨进程的操作吗? 实际上在Android开发中,Activity A启动Activity B通过Intent传递数据。在这个过程中需要离开应用程序所在进程,转而调用native方法进入linux kernel进程中执行Activity切换的实际操作。因此在这个过程中数据是跨进程进行的,所以需要对传递的对象进行序列化操作。

Serializable

Serializable是在包java.io下,它是interface。 打开源码一看,一脸懵。什么啊接口为空,啥都没。它是怎么序列化的?

public interface Serializable {
}

看看官方介绍吧,implements Serializable的类可以通过ObjectOutputStream序列化,通过ObjectInputStream反序列化。那我们就通过源码看看大体上是如何通过两个类去做序列化和反序列化。

ObjectOutputStream

ObjectOutputStream继承于抽象类OutputStream。对于对Serializable序列化过程在write方法下,在writeObject方法中有一个writeObject0,下面讲一下它。 ObjectOutputStream支持对Serializable类型的Object写操作。在方法中会去执行writeOrdinaryObject去对Serializable做写操作。然后在writeOrdinaryObject中可以看到会去判断对象是Externalizable还是Serializable,根据类型不同在去做不同的写操作。Externalizable是Serializable的子类,它提供两个接口方法:writeExternal、readExternal。

...
            if (obj instanceof Class) {
                writeClass((Class) obj, unshared);
            } else if (obj instanceof ObjectStreamClass) {
                writeClassDesc((ObjectStreamClass) obj, unshared);
            // END Android-changed
            } 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());
                }
            }
...

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);//BlockDataOutputStream写入类型标记该对象为TC_OBJECT
            writeClassDesc(desc, false);
            handles.assign(unshared ? null : obj);
            if (desc.isExternalizable() && !desc.isProxy()) {
                writeExternalData((Externalizable) obj);
            } else {
                writeSerialData(obj, desc);
            }
        } finally {
            if (extendedDebugInfo) {
                debugInfoStack.pop();
            }
        }
    }

ObjectInputStream

ObjectOutputStream继承于抽象类OutputStream。对应ObejctOutStream,对Seriallizable的反序列化过程在readObejct中。同样readObejct有readObject0。readObject0下面我们很清楚的看到同样有readOrdinaryObject。在readOrdinaryObject还是和上述一样会去判断是Serializable还是子类Externalizable去执行不同的读取方法。

private Object readObject0(boolean unshared) throws IOException {
      ...
        depth++;
        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));

                case TC_OBJECT://通过BlockDataInputStream的peekByte拿到对象类型知道该对象为Serializable
                    return checkResolve(readOrdinaryObject(unshared));

           ....
    }


        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()) {//判断是子类还是Serializable本身                                                  
            readExternalData((Externalizable) obj, desc);                               
        } else {                                                                        
            readSerialData(obj, desc);                                                  
        }                                                                               
                                                                                        
        handles.finish(passHandle);                                                     
                                                                                        
        if (obj != null &&                                                              
            handles.lookupException(passHandle) == null &&                              
            desc.hasReadResolveMethod())                                                
        {                                                                               
            Object rep = desc.invokeReadResolve(obj);                                   
            if (unshared && rep.getClass().isArray()) {                                 
                rep = cloneArray(rep);                                                  
            }                                                                           
            if (rep != obj) {                                                           
                handles.setObject(passHandle, obj = rep);                               
            }                                                                           
        }                                                                               
                                                                                        
        return obj;                                                                     
    }                                                                                   
  • Serializable在序列化中开销比较大,过程会产生大量临时变量,从而引起频繁的GC。虽然Serializable可以将数据持久化,但内存序列化太占用资源。
  • 静态成员变量属于类不属于对象因此不参与序列化过程。
  • transient可以屏蔽特殊数据成员不参与序列化。

serialVersionUID的详细工作机制是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的文件中,当反序列化的时候系统会去检查文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致说明序列化的类版本与当前类的版本是相同的则可以成功反序列化。最好是实现Serializable接口的对象添加UID,若反序列化时原有类发生改变,增加或删除某些成员变量,那么系统要重新计算当前类的hash值。然而当前是UID和序列化数据的UID对应不上会出现反序列失败。

Parcelable

Parcelable是在包android.os下的,同样它也是interface。话说Android设计Parcelable初衷就是因为Serializable序列化效率慢。而且Android很多类都实现Parcelable接口。

    @Retention(RetentionPolicy.SOURCE)
    public @interface ContentsFlags {}
    public @ContentsFlags int describeContents();
    public void writeToParcel(Parcel dest, @WriteFlags int flags);
    public interface Creator<T> {
        public T createFromParcel(Parcel source);
        public T[] newArray(int size);
    }
    public interface ClassLoaderCreator<T> extends Creator<T> {
        public T createFromParcel(Parcel source, ClassLoader loader);
    }

下面的DrivderParcelable实现Parcelable接口,可以看到类成员写入Parcel和从Parcel读出顺序必须保持一致。

public class DrivderParcelable implements Parcelable{

    private String name;
    private String address;
    private String carName;

    public DrivderParcelable(String name, String address, String carName) {
        this.name = name;
        this.address = address;
        this.carName = carName;
    }


    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.name);
        dest.writeString(this.address);
        dest.writeString(this.carName);
    }

    protected DrivderParcelable(Parcel in) {
        this.name = in.readString();
        this.address = in.readString();
        this.carName = in.readString();
    }

    public static final Creator<DrivderParcelable> CREATOR = new Creator<DrivderParcelable>() {
        @Override
        public DrivderParcelable createFromParcel(Parcel source) {
            return new DrivderParcelable(source);
        }

        @Override
        public DrivderParcelable[] newArray(int size) {
            return new DrivderParcelable[size];
        }
    };
}

Parcel

其实实现Parcelable接口,其中读写方法还都是在Parcel中。writeToParcel是序列化过程方法,ClassLoaderCreator是反序列化过程,所以Parcelable序列化过程都是交给Parcel实现的。查看Parcel源码会发现,很多方法在native层实现的,甚至读写方法。因为整个过程都在内存中操作,调用malloc()、realloc()、memcpy()内存操作,因此效率会比在Java序列化操作使用外部存储更加高效。

...
    private static native void nativeWriteDouble(long nativePtr, double val);
    private static native void nativeWriteString(long nativePtr, String val);
    private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
    private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
...
    @FastNative
    private static native int nativeReadInt(long nativePtr);
    @FastNative
    private static native long nativeReadLong(long nativePtr);
    @FastNative
    private static native float nativeReadFloat(long nativePtr);
    @FastNative
    private static native double nativeReadDouble(long nativePtr);
    private static native String nativeReadString(long nativePtr);
...

另外会发现Parcel支持写入的对象很多,包括Serializable、IBinder。相比较会比ObjectOutputStream更为强大啊!

public final void writeValue(Object v) {
        if (v == null) {
            writeInt(VAL_NULL);
        } else if (v instanceof String) {
            writeInt(VAL_STRING);
            writeString((String) v);
        } else if (v instanceof Integer) {
            writeInt(VAL_INTEGER);
            writeInt((Integer) v);
        } else if (v instanceof Map) {
            writeInt(VAL_MAP);
            writeMap((Map) v);
        } else if (v instanceof Bundle) {
            // Must be before Parcelable
            writeInt(VAL_BUNDLE);
            writeBundle((Bundle) v);
        } else if (v instanceof PersistableBundle) {
            writeInt(VAL_PERSISTABLEBUNDLE);
            writePersistableBundle((PersistableBundle) v);
        } else if (v instanceof Parcelable) {
            // IMPOTANT: cases for classes that implement Parcelable must
            // come before the Parcelable case, so that their specific VAL_*
            // types will be written.
            writeInt(VAL_PARCELABLE);
            writeParcelable((Parcelable) v, 0);
        } else if (v instanceof Short) {
            writeInt(VAL_SHORT);
            writeInt(((Short) v).intValue());
        } else if (v instanceof Long) {
            writeInt(VAL_LONG);
            writeLong((Long) v);
        } else if (v instanceof Float) {
            writeInt(VAL_FLOAT);
            writeFloat((Float) v);
        } else if (v instanceof Double) {
            writeInt(VAL_DOUBLE);
            writeDouble((Double) v);
        } else if (v instanceof Boolean) {
            writeInt(VAL_BOOLEAN);
            writeInt((Boolean) v ? 1 : 0);
        } else if (v instanceof CharSequence) {
            // Must be after String
            writeInt(VAL_CHARSEQUENCE);
            writeCharSequence((CharSequence) v);
        } else if (v instanceof List) {
            writeInt(VAL_LIST);
            writeList((List) v);
        } else if (v instanceof SparseArray) {
            writeInt(VAL_SPARSEARRAY);
            writeSparseArray((SparseArray) v);
        } else if (v instanceof boolean[]) {
            writeInt(VAL_BOOLEANARRAY);
            writeBooleanArray((boolean[]) v);
        } else if (v instanceof byte[]) {
            writeInt(VAL_BYTEARRAY);
            writeByteArray((byte[]) v);
        } else if (v instanceof String[]) {
            writeInt(VAL_STRINGARRAY);
            writeStringArray((String[]) v);
        } else if (v instanceof CharSequence[]) {
            // Must be after String[] and before Object[]
            writeInt(VAL_CHARSEQUENCEARRAY);
            writeCharSequenceArray((CharSequence[]) v);
        } else if (v instanceof IBinder) {
            writeInt(VAL_IBINDER);
            writeStrongBinder((IBinder) v);
        } else if (v instanceof Parcelable[]) {
            writeInt(VAL_PARCELABLEARRAY);
            writeParcelableArray((Parcelable[]) v, 0);
        } else if (v instanceof int[]) {
            writeInt(VAL_INTARRAY);
            writeIntArray((int[]) v);
        } else if (v instanceof long[]) {
            writeInt(VAL_LONGARRAY);
            writeLongArray((long[]) v);
        } else if (v instanceof Byte) {
            writeInt(VAL_BYTE);
            writeInt((Byte) v);
        } else if (v instanceof Size) {
            writeInt(VAL_SIZE);
            writeSize((Size) v);
        } else if (v instanceof SizeF) {
            writeInt(VAL_SIZEF);
            writeSizeF((SizeF) v);
        } else if (v instanceof double[]) {
            writeInt(VAL_DOUBLEARRAY);
            writeDoubleArray((double[]) v);
        } else {
            Class<?> clazz = v.getClass();
            if (clazz.isArray() && clazz.getComponentType() == Object.class) {
                // Only pure Object[] are written here, Other arrays of non-primitive types are
                // handled by serialization as this does not record the component type.
                writeInt(VAL_OBJECTARRAY);
                writeArray((Object[]) v);
            } else if (v instanceof Serializable) {
                // Must be last
                writeInt(VAL_SERIALIZABLE);
                writeSerializable((Serializable) v);
            } else {
                throw new RuntimeException("Parcel: unable to marshal value " + v);
            }
        }
    }

因此可以猜到实现Parcelable接口对象中的成员变量可以是实现Serializable接口的对象。但子类实现的接口必需和父类统一,否则序列化失败。

  • 比起Serializable,Parcelable开销更小效率更高。
  • 序列化过程较复杂。其实实现Parcelable接口的方法是可以用插件直接生成的。
  • Parcelable实现主要还是解决Android平台内存紧缺的问题。

最后

总结来说Serializable更适合稳定的数据传递、持久化以及网络传输,主要还是因为Parcelable是有Android开发而产生可能在多个版本中存在差异,为了兼容性以及稳定性做好选择Serializable。Parcelable虽然实现复杂,但它更高效,占用内存小,因此适合在内存序列化中使用,例如Activity间通信和AIDL数据传递。