This is the 6th day of my participation in the August More Text Challenge
start
The reason for serialization is that Java inter-process communication data cannot be transferred directly, so it must be serialized and then deserialized to get the original data. In Android development, objects passed by an Intent in the same application need to be serialized! Are there cross-process operations? In fact, in Android development, Activity A starts Activity B and passes data with an Intent. In this process, it is necessary to leave the process where the application is located and call the native method to enter the Linux Kernel process to perform the actual operation of Activity switching. So the data is cross-process in this process, so you need to serialize the passed object.
Serializable
Serializable is under the package java.io, which is interface. Open the source code, a face meng. What? The interface’s empty. There’s nothing. How is it serialized?
public interface Serializable {
}
Copy the code
Implements Serializable classes can be serialized with ObjectOutputStream and deserialized with ObjectInputStream. Let’s take a look at the source code to see how serialization and deserialization can be done with two classes in general.
ObjectOutputStream
ObjectOutputStream inherits from the abstract OutputStream class. For the Serializable serialization process under the write method, there is a writeObject0 in the writeObject method, which is described below. ObjectOutputStream supports write operations to Serializable Object. In the method writeOrdinaryObject is executed to write to Serializable. The writeOrdinaryObject will determine whether the object is Externalizable or Serializable, and perform different write operations according to the type. Externalizable is a subclass of Serializable and provides two interface methods: writeExternal and 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 instanceofEnum) { 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 newNotSerializableException(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 Write type marks this object as 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(); }}}Copy the code
ObjectInputStream
ObjectOutputStream inherits from the abstract OutputStream class. Corresponding to ObejctOutStream, the deserialization of Seriallizable is in readObejct. Again, readObejct has readObject0. ReadObject0 below we clearly see that there is also a readOrdinaryObject. ReadOrdinaryObject as above will check whether Serializable or subclass Externalizable executes a different read method.
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:// Get the object type from peekByte of BlockDataInputStream to know that the object is Serializable
returncheckResolve(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()) {// Determine whether it is a subclass or Serializable itself
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;
}
Copy the code
- Serializable is expensive in serialization, with a large number of temporary variables resulting in frequent GC. Although Serializable can persist data, memory serialization is too resource-intensive.
- Static member variables belong to classes, not objects and therefore do not participate in the serialization process.
- Transient can shield special data members from serialization.
Here’s how the serialVersionUID works in detail: During serialization, the system will write the serialVersionUID of the current class into the serialized file. During deserialization, the system will check the serialVersionUID in the file to see whether it is the same as the serialVersionUID of the current class. Deserialization can be successful if it is consistently stated that the version of the serialized class is the same as the version of the current class. It is best to add a UID to an object that implements the Serializable interface. If the original class changes during deserialization, adding or removing some member variables, then the system recalculates the hash value of the current class. However, the current UID does not correspond to the UID of the serialized data.
Parcelable
Parcelable is under package Android. OS, which is also interface. Android was designed for Parcelable because Serializable is slow. And many Android classes implement the Parcelable interface.
@Retention(RetentionPolicy.SOURCE)
public @interface ContentsFlags {}
public @ContentsFlags int describeContents(a);
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);
}
Copy the code
The following DrivderParcelable implements the Parcelable interface and you can see that class members must write to and read from the Parcel in the same order.
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(a) {
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 newDrivderParcelable[size]; }}; }Copy the code
Parcel
Implement the Parcelable interface where the read and write methods are all in the Parcel. WriteToParcel is the serialization process method and ClassLoaderCreator is the deserialization process, so Parcelable serialization is handed over to the Parcel implementation. A look at the source code for a Parcel reveals that many methods are implemented in the native layer, even read-write methods. Because the entire process operates in memory, calling malloc(), Realloc (), memcpy() memory operations, it is more efficient than using external storage for Java serialization operations.
.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); .Copy the code
Additionally, you’ll find that Parcel supports writing to many objects, including Serializable and IBinder. ObjectOutputStream is more powerful than 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); }}}Copy the code
So you can guess that a member variable in an object implementing the Parcelable interface can be an object implementing the Serializable interface. However, the subclass must implement the same interface as the parent class, otherwise serialization will fail.
- Parcelable is less expensive and more efficient than Serializable.
- The serialization process is complicated. The methods that implement the Parcelable interface can be generated directly from plug-ins.
- The Parcelable implementation mainly solves the memory shortage problem of the Android platform.
The last
In summary, Serializable is more suitable for stable data transfer, persistence and network transmission, mainly because Parcelable is developed by Android and may have differences in multiple versions, for compatibility and stability to choose Serializable. Although Parcelable is complex to implement, it is more efficient and takes up less memory, making it suitable for use in memory serialization, such as communication between activities and AIDL data passing.