Java序列化

Android序列化

目标

为什么序列化

序列化的定义

Serializable的使用和原理

Parcelable的使用和原理

Serializable和Parcelable比较

为什么序列化

由于在系统底层,数据的传输形式是简单的字节序列形式传递,即在底层,系统不认识对象,只认识字节序列,而为了达到进程通讯的目的,需要先将数据序列化,而序列化就是将对象转化字节序列的过程。相反地,当字节序列被运到相应的进程的时候,进程为了识别这些数据,就要将其反序列化,即把字节序列转化为对象

序列化的定义

基本定义:将一个类对象转换成可存储、可传输状态的过程。也就是将数据结构或对象转换成二进制串的过程

反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程

Serializable的使用和原理

是 Java 提供的序列化接口,它是一个空接口:

public interface Serializable { }

Serializable只起到了一个标识的作用,用于告知程序实现了Serializable的对象是可以被序列化的,但真正进行序列化和反序列化的操作是通过ObjectOutputStream及ObjectInputStream实现的。

Serializable使用


    public class Student implements Serializable {
        //serialVersionUID唯一标识了一个可序列化的类 
        private static final long serialVersionUID = -2100492893943893602L;
        private String name;
        private String sax;
        private Integer age;
        //Course也需要实现Serializable接口 
        private List courses;
//用transient关键字标记的成员变量不参与序列化(在被反序列化后,transient 变量的值被 
        设为初始值,如 int 型的是 0,对象型的是 null)
        private transient Date createTime;
//静态成员变量属于类不属于对象,所以不会参与序列化(对象序列化保存的是对象的“状态”,也 
        就是它的成员变量,因此序列化不会关注静态变量)
        private static SimpleDateFormat simpleDateFormat = new
                SimpleDateFormat();
        public Student() {
            System.out.println("Student: empty");
        }
        public Student(String name, String sax, Integer age) {
            System.out.println("Student: " + name + " " + sax + " " + age);
            this.name = name;
            this.sax = sax;
            this.age = age;
            courses = new ArrayList<>();
            createTime = new Date();
        }
...
    }
    ////Course也需要实现Serializable接口 
    public class Course implements Serializable {
        private static final long serialVersionUID = 667279791530738499L;
        private String name;
        private float score; 
...
    }

Serializable 注意点:

1.用transient关键字标记的成员变量不参与序列化(在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null)

2.静态成员变量属于类不属于对象,所以不会参与序列化(对象序列化保存的是对象的“状态”,也就是它的成员变量,因此序列化不会关注静态变量)

3.一个实现序列化的类,它引用到的类也是可序列化的

serialVersionUID

目的:兼容性问题

为了在反序列化时,确保类版本的兼容性,最好在每个要序列化的类中加入 private static fifinal long serialVersionUID这个属性,具体数值自己定义。这样,即使某个类在与之对应的对象 已经序列化出去后做了修改,该对象依然可以被正确反序列化。否则,如果不显式定义该属性,这个属性值将由JVM根据类的相关信息计算,而修改后的类的计算 结果与修改前的类的计算结果往往不同,从而造成对象的反序列化因为类版本不兼容而失败。

不显式定义这个属性值的另一个坏处是,不利于程序在不同的JVM之间的移植。因为不同的编译器实现该属性值的计算策略可能不同,从而造成虽然类没有改变,但是因为JVM不同,出现因类版本不兼容而无法正确反序列化的现象出现

因此 JVM 规范强烈 建议我们手动声明一个版本号,这个数字可以是随机的,只要固定不变就可以。同时最好是 private 和 fifinal 的,尽量保证不变。

实现自定义序列化

Serializable实现自定义序列化必须重写readObject、writeObject方法,readResolve、writeReplace方法是可选的,看下面的例子:

public class S_Shop implements Serializable{
private static final long serialVersionUID = -1399695071515887643L;
public transient String mShopName;//注意mShopName是瞬态的
public int mShopId;
public String mShopPhone;

/**
 * 序列化时执行 执行顺序早于writeObject 可以在此方法中做一些替换
 */
private Object writeReplace() {
    System.out.println("-----writeReplace() start-----");
    S_Shop shop = new S_Shop();
    shop.mShopName = "物美超市";//将mShopName替换
    shop.mShopId = mShopId;
    shop.mShopPhone = mShopPhone;
    return shop;
}

/**
 * 序列化时执行 通过defaultWriteObject将非transient字段序列化 也可以自定义序列化字段
 */
private void writeObject(ObjectOutputStream outputStream) throws IOException {
    System.out.println("-----writeObject() start-----");
    outputStream.defaultWriteObject();
    outputStream.writeObject(mShopName);
}

/**
 * 反序列化时执行 通过defaultReadObject将非transient字段反序列化 也可以将自定义字段反序列化
 */
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
    System.out.println("-----readObject() start-----");
    inputStream.defaultReadObject();
    mShopName = (String) inputStream.readObject();
}

/**
 * 反序列化时执行,执行顺序在readObject之后 可以在此方法中重新生成一个新对象
 */
private Object readResolve() {
    System.out.println("-----readResolve() start-----");
    S_Shop shop = new S_Shop();
    shop.mShopName = mShopName;
    shop.mShopId = mShopId;
    shop.mShopPhone = "12345678";//将mShopPhone替换
    return shop;
}

@NonNull
@Override
public String toString() {
    return "Serializable: mShopName is " + mShopName
            + ",mShopId is " + mShopId
            + ",mShopPhone is " + mShopPhone;
}
}

执行结果:

修改前:Serializable: mShopName is 便利蜂,mShopId is 2020,mShopPhone is 18888888888
-----writeReplace() start-----
-----writeObject() start-----
-----readObject() start-----
-----readResolve() start-----
修改后:Serializable: mShopName is 物美超市,mShopId is 2020,mShopPhone is 12345678

序列化过程的执行顺序:writeReplace->writeObject;反序列化过程的执行顺序:readObject->readResolve 通过上面四个方法,可以实现Serializable的自定义序列化。

注:虽然上述的四个方法都是private级别的,但在反序列化过程中是通过反射执行的。

Serializable序列化过程

序列化算法一般会按步骤做如下事情:

将对象实例相关的类元数据输出。

递归地输出类的超类描述直到不再有超类。

类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值。

从上至下递归输出实例的数据

反序列化分别通过 ObjectOutputStream 和 ObjectInputStream 进行

writeObject源码解读

1.ObjectOutputStream的构造函数

设置enableOverride = false

创建BlockDataOutputStream对象

 public ObjectOutputStream(OutputStream out) throws IOException {
        verifySubclass();
        bout = new BlockDataOutputStream(out);
        handles = new HandleTable(10, (float) 3.00);
        subs = new ReplaceTable(10, (float) 3.00);
        enableOverride = false;//enableOverride = false 
...
    }

2.所以writeObject方法执行的是writeObject0(obj, false);

    public final void writeObject(Object obj) throws IOException {
//enableOverride=false,不走这里 
        if (enableOverride) {
            writeObjectOverride(obj);
            return;
        }
        try {//一般情况都走这里 
            writeObject0(obj, false); 
...
        }
    }

3.writeObject0

记录原始的序列化对象

创建新的序列化对象class标记的对象

获取标记对象创建ObjectStreamClass,用于记录序列化对象obj的属性Class信息

通过ReplaceTable记录替换只记录了class信息的obj,此时obj便记录了序列化的属性和值

调用writeOrdinaryObject

 private void writeObject0(Object obj, boolean unshared)
        throws IOException
    {
        try {

            // 记录原始的序列化对象
            Object orig = obj;
            
            //获取序列化对象的Class,然后通过这个Class创建ObjectStreamClass,用于记录序列化对象obj的属性Class信息
            Class cl = obj.getClass();
            ObjectStreamClass desc;

            // BEGIN Android-changed: Make only one call to writeReplace.
            /*
            for (;;) {
                // REMIND: skip this check for strings/arrays?
                Class repCl;
                desc = ObjectStreamClass.lookup(cl, true);
                if (!desc.hasWriteReplaceMethod() ||
                    (obj = desc.invokeWriteReplace(obj)) == null ||
                    (repCl = obj.getClass()) == cl)
                {
                    break;
                }
                cl = repCl;
                desc = ObjectStreamClass.lookup(cl, true);
                break;
            }
            //通过ReplaceTable记录替换只记录了class信息的obj,此时obj便记录了序列化的属性和值
            if (obj != orig) {
                subs.assign(orig, obj);
               
            }

            // remaining cases
            // BEGIN Android-changed: Make Class and ObjectStreamClass replaceable.
            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);
                //writeOrdinaryObject执行开始对象写入
            } 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());
                }
            }
        } finally {
            depth--;
            bout.setBlockDataMode(oldMode);
        }
    }

4.writeOrdinaryObject(obj, desc, unshared)

如果对象实现了Externalizable接口,那么执行 writeExternalData

如果对象实现的是Serializable接口,那么执行的是writeSerialData

   private void writeOrdinaryObject(Object obj,
                                     ObjectStreamClass desc,
                                     boolean unshared) 
...
        if (desc.isExternalizable() && !desc.isProxy()) {
//如果对象实现了Externalizable接口,那么执行 
        writeExternalData((Externalizable) obj)方法
        writeExternalData((Externalizable) obj);
    } else {//如果对象实现的是Serializable接口,那么执行的是 
        writeSerialData(obj, desc)
        writeSerialData(obj, desc);
    } 
...
}
//这里我们看看writeExternalData

5.writeSerialData方法,主要执行方法:defaultWriteFields(obj, slotDesc)

通过ObjectStreamClass实例获取slots,遍历slots获取slotDesc,也是ObjectStreamClass

使用slotDesc(ObjectStreamClass)invokeWriteObject,也就是如果writeObjectMethod != null(目标类中定义了私有的writeObject方法),那么将调用目标类中的writeObject方法

默认的走defaultWriteFields

    /**
     * Writes instance data for each serializable class of given object, 
     from 
     * superclass to subclass. 
     * 最终写序列化的方法 
     */
    private void writeSerialData(Object obj, ObjectStreamClass desc)
            throws IOException
    { 
         ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
         ....
          ObjectStreamClass slotDesc = slots[i].desc;
        if (slotDesc.hasWriteObjectMethod()) {
//如果writeObjectMethod != null(目标类中定义了私有的writeObject方法),那么将调用目标类中的writeObject方法 
...
            slotDesc.invokeWriteObject(obj, this); 
...
        } else {
            //如果如果writeObjectMethod == null, 那么将调用默认的 
            defaultWriteFields方法来读取目标类中的属性
            defaultWriteFields(obj, slotDesc);
        }
    }

6.defaultWriteFields方法

desc.getPrimFieldValues(obj, primVals);

desc.getObjFieldValues(obj, objVals);

获取ObjectStreamClass保存的属性,遍历调用writeObject0完成

 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();
                }
            }
        }
    }

7.writeObject0方法中基本类型写入

 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);
            } 
private void writeString(String str, boolean unshared) throws IOException {
        handles.assign(unshared ? null : str);
        long utflen = bout.getUTFLength(str);
        if (utflen <= 0xFFFF) {
            bout.writeByte(TC_STRING);
            bout.writeUTF(str, utflen);
        } else {
            bout.writeByte(TC_LONGSTRING);
            bout.writeLongUTF(str, utflen);
        }
    }

最终调用BlockDataOutputStream的write方法

BlockDataOutputStream extends OutputStream

所以最终数据通过OutputStream被写入到文件

ObjectStreamClass

序列化类的描述符。它包含类的名称和serialVersionUID。它由Java VM加载,可以使用lookup方法找到或创建。

内部有个ObjectStreamFields数组,用来记录目标对象的内部变量(内部变量可以是基本类型,也可以是自定义类型,但是必须都支持序列化—必须是S不能是P)。

 /**
     * Creates local class descriptor representing given class.
     */
    private ObjectStreamClass(final Class cl) {
...
        if (externalizable) {
            cons = getExternalizableConstructor(cl);
        } else {
            //,在序列化(反序列化)的时候,ObjectOutputStream(ObjectInputStream)
            // 会寻找目标类中的私有的writeObject(readObject)方法,
            // 赋值给变量writeObjectMethod(readObjectMethod)
            cons = getSerializableConstructor(cl);
            writeObjectMethod = getPrivateMethod(cl,
                    "writeObject",
                    new Class[] { ObjectOutputStream.class },
                    Void.TYPE);
            readObjectMethod = getPrivateMethod(cl,
                    "readObject",
                    new Class[] { ObjectInputStream.class },
                    Void.TYPE);
            readObjectNoDataMethod = getPrivateMethod(
                    cl, "readObjectNoData", null, Void.TYPE);
            hasWriteObjectData = (writeObjectMethod != null);
        }
        domains = getProtectionDomains(cons, cl);
        writeReplaceMethod = getInheritableMethod(
                cl, "writeReplace", null, Object.class);
        readResolveMethod = getInheritableMethod(
                cl, "readResolve", null, Object.class);
        return null;
    }
});
        ...
        }
//ObjectStreamClass类中的一个判断方法
        boolean hasWriteObjectMethod() {
        requireInitialized();
        return (writeObjectMethod != null);
        }

ObjectStreamField: 类(可序列化)的可序列化字段的描述。ObjectStreamFields数组用于声明类的可序列化字段。

序列化过程

1.通过ObjectStreamClass记录目标对象的类型、类名等信息,内部有个ObjectStreamFields数组,用来记录目标对象的内部变量(内部变量可以是基本类型,也可以是自定义类型,但是必须都支持序列化—必须是S不能是P)。

2.首先通过ObjectStreamClass.lookup()找到或创建ObjectStreamClass,然后调用defaultWriteFields方法,在方法中通过getPrimFieldValues()获取基本数据类型并赋值到primVals(byte[]类型)中,再通过getObjFieldValues()获取到自定义对象(通过Unsafe类实现而不是反射)并赋值到objVals(Object[]类型)中,接着遍历objVals数组,然后递归调用writeObject0

3.将对象的信息和属性名、属性值通过IO(OutputStream)写入文件

反序列化过程(readObject方法)

通过readClassDescriptor()读取InputStream里的数据并初始化ObjectStreamClass类,再根据这个实例通过反射创建目标对象实例。

Serializable需要注意的坑

多引用写入

   public class Course implements Serializable {
        private static final long serialVersionUID = 667279791530738499L;
        private String name;
        private float score; 
...
        public static void main(String... args) throws Exception {
//TODO: 
//TODO: 
            Course course = new Course("英语", 12f);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(out);
            oos.writeObject(course);
            course.setScore(78f);
// oos.reset(); 
            oos.writeUnshared(course);
// oos.writeObject(course); 
            byte[] bs = out.toByteArray();
            oos.close();
            ObjectInputStream ois = new ObjectInputStream(new
                    ByteArrayInputStream(bs));
            Course course1 = (Course) ois.readObject();
            Course course2 = (Course) ois.readObject();
            System.out.println("course1: " + course1);
            System.out.println("course2: " + course2);
        }
    }

执行结果:

course1: Course{name='英语', score=12.0} 
course2: Course{name='英语', score=12.0} 

在默认情况下, 对于一个实例的多个引用,为了节省空间,只会写入一次,后面会追加几个字节代表某个实例的引用。

子类实现序列化,父类不实现序列化/ 对象引用

public class Person {
        private String name;
        private String sax;
        // public Person() { 
// } 
        public Person(String name, String sax) {
            this.name = name;
            this.sax = sax;
        }
    }
    public class Student1 extends Person implements Serializable {
        private static final long serialVersionUID = -2100492893943893602L;
        private Integer age;
        private List courses;
        public Student1(String name, String sax, Integer age) {
            super(name,sax); 
...
        } 
...
        public static void main(String ... args) throws Exception{
//TODO: 
            Student1 student = new Student1("Zero", "男", 18);
            student.addCourse(new Course("语文", 90.2f));
//序列化 
            byte[] bytes = SerializeableUtils.serialize(student);
            System.out.println(Arrays.toString(bytes));
//反序列化 
//在readObject时抛出java.io.NotSerializableException异常。 
//需要Person添加一个无参数构造器 
            Student1 student1 = SerializeableUtils.deserialize(bytes);
            System.out.println("Student: " + student1);
        }

在readObject时抛出java.io.NotSerializableException异常。

类的演化

    //反序列化目标类多一个字段(height) 
    public class Student implements Serializable {
        private static final long serialVersionUID = -2100492893943893602L;
        private String name;
        private String sax;
        private Integer age;
        private List courses; 
...
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", sax='" + sax + '\'' +
                    ", age=" + age +
// ", height=" + height + 
                    ", courses=" + courses +
                    '}';
        }
        //private float height; 
        public static void main(String ... args)throws Exception{
//TODO: 
            String path = System.getProperty("user.dir") +"/a.out";
// Student student = new Student("Zero", "男", 18); 
// student.addCourse(new Course("语文", 90.2f)); 
// //序列化 
// SerializeableUtils.saveObject(student,path); 
//反序列化 
            Student student1 = SerializeableUtils.readObject(path);
            System.out.println("Student: " + student1);
        }
    }

执行结果:

//序列化的时候 
Student: Zero 男 18 
Course: 语文 90.2 
//反序列化的时候 添加一个float height 
Student: Student{name='Zero', sax='男', age=18, height=0.0, courses= 
[Course{name='语文', score=90.2}]} 

可以看出反序列化之后,并没有报错,只是height实赋成了默认值。类似的其它对象也会赋值为默认

值。

还有 相反,如果写入的多一个字段,读出的少一个字段,也是不会报错的

其它演化,比如更改类型等,这种演化本身就有问题,没必再探讨

枚举类型

    public enum Num {
        TWO, ONE, THREE;
        public void printValues() {
            System.out.println(ONE + " ONE.ordinal " + ONE.ordinal());
            System.out.println(TWO + " TWO.ordinal " + TWO.ordinal());
            System.out.println(THREE + " THREE.ordinal " + THREE.ordinal());
        }
        public static void testSerializable() throws Exception {
            File file = new File("p.dat");
// ObjectOutputStream oos = new ObjectOutputStream(new 
            FileOutputStream(file));
// oos.writeObject(Num.ONE); 
// oos.close(); 
            Num.ONE.printValues();
            System.out.println("=========反序列化后=======");
            ObjectInputStream ois = new ObjectInputStream(new
                    FileInputStream(file));
            Num s1 = (Num) ois.readObject();
            s1.printValues();
            ois.close();
        }
        public static void main(String... args) throws Exception {
//TODO: 
            testSerializable();
        }
    }

执行结果:

ONE ONE.ordinal 1 
TWO TWO.ordinal 0 
THREE THREE.ordinal 2 
=========反序列化后======= 
//调换(ONE,TWO)的位置: TWO, ONE, THREE; ->ONE, TWO, THREE; 
ONE ONE.ordinal 0 
TWO TWO.ordinal 1 
THREE THREE.ordinal 2 

可以看到ONE的值变成了0.

事实上序列化Enum对象时,并不会保存元素的值,只会保存元素的name。这样,在不依赖元素值的

前提下,ENUM对象如何更改都会保持兼容性。

单例模式的序列化问题反射问题

    public class SingleTest {
        static final String CurPath = System.getProperty("user.dir");
        public static void main(String ... args) throws Exception {
//TODO: 
            Single instance = Single.getInstance();
            System.out.println(instance.hashCode());
            System.out.println(copyInstance(instance).hashCode());
            System.out.println("=================反射======================");
//使用反射方式直接调用私有构造器 
            Class clazz =
                    (Class)Class.forName("com.zero.serializabledemo.serializable.Single"
                    );
            Constructor con = clazz.getDeclaredConstructor(null);
            con.setAccessible(true);//绕过权限管理,即在true的情况下,可以通过构造函数 
            新建对象
            Single instance1 = con.newInstance();
            Single instance2 = con.newInstance();
            System.out.println(instance1.hashCode());
            System.out.println(instance2.hashCode());
        }
        private static Single copyInstance(Single instance) throws Exception{
//序列化会导致单例失效 
            FileOutputStream fos = new FileOutputStream(CurPath+"/a.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(instance);
            ObjectInputStream ois = new ObjectInputStream(new
                    FileInputStream(CurPath+"/a.txt"));
            Single single2 = (Single)ois.readObject();
            oos.close();
            ois.close();
            return single2;
        }
    }
    class Single implements Serializable {
        private static final long serialVersionUID = 1L;
        private static boolean flag = false;
        private Single(){
            synchronized (Single.class) {
                if (!flag) {
// flag = true; 
                } else {
                    throw new RuntimeException("单例模式被侵犯!");
                }
            }
        }
        private static Single single;
        public static Single getInstance(){
            if ( single == null ) {
                synchronized (Single.class) {
                    if ( single == null ) {
                        single = new Single();
                    }
                }
            }
            return single;
        }
//如果不重写readResolve,会导致单例模式在序列化->反序列化后失败 
// private Object readResolve() { 
// return single; 
// } 
    }

Parcelable接口

介绍Parcelable不得不先提一下Serializable接口,Serializable是Java为我们提供的一个标准化的序列化

接口,那什么是序列化呢? ---- 简单来说就是将对象转换为可以传输的二进制流(二进制序列)的过程,这样

我们就可以通过序列化,转化为可以在网络传输或者保存到本地的流(序列),从而进行传输数据 ,那反序列

化就是从二进制流(序列)转化为对象的过程.

Parcelable是Android为我们提供的序列化的接口,Parcelable相对于Serializable的使用相对复杂一些,但

Parcelable的效率相对Serializable也高很多,这一直是Google工程师引以为傲的,有时间的可以看一下

Parcelable和Serializable的效率对比 Parcelable vs Serializable 号称快10倍的效率

Parcelable是Android SDK提供的,它是基于内存的,由于内存读写速度高于硬盘,因此Android中的

跨进程对象的传递一般使用Parcelable

Parcelable简介

Parcel翻译过来是打包的意思,其实就是包装了我们需要传输的数据,然后在Binder中传输,也就是用于跨进程传输数据

简单来说,Parcel提供了一套机制,可以将序列化之后的数据写入到一个共享内存中,其他进程通过Parcel可以从这块共享内存中读出字节流,并反序列化成对象,下图是这个过程的模型。

Parcelable的使用

    public class Course implements Parcelable {
        private String name;
        private float score; 
...
        /**
         * 描述当前 Parcelable 实例的对象类型 
         * 比如说,如果对象中有文件描述符,这个方法就会返回上面的 
         CONTENTS_FILE_DESCRIPTOR 
         * 其他情况会返回一个位掩码 
         * @return
         */
        @Override
        public int describeContents() {
            return 0;
        }
        /**
         * 将对象转换成一个 Parcel 对象 
         * @param dest 表示要写入的 Parcel 对象 
         * @param flags 示这个对象将如何写入 
         */
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(this.name);
            dest.writeFloat(this.score);
        }
        protected Course(Parcel in) {
            this.name = in.readString();
            this.score = in.readFloat();
        }
        /**
         * 实现类必须有一个 Creator 属性,用于反序列化,将 Parcel 对象转换为 Parcelable 
         * @param 
         */
        public static final Parcelable.Creator CREATOR = new
                Parcelable.Creator() {
                    //反序列化的方法,将Parcel还原成Java对象 
                    @Override
                    public Course createFromParcel(Parcel source) {
                        return new Course(source);
                    }
                    //提供给外部类反序列化这个数组使用。 
                    @Override
                    public Course[] newArray(int size) {
                        return new Course[size];
                    }
                };
    }

Parcel的原理

Parcel可以被认为是一个包含数据或者对象引用的容器,能够支持序列化及在跨进程之后的反序列化。P的序列化操作在Native层实现,通过write内存写入及read读内存数据重新生成对象。P将对象进行分解,且分解后每一部分都是支持可传递的数据类型。

序列化过程(Parcelable的写过程)

调用过程Parcel.writeValue()->writeParcelable(),下面主要来看下此方法:

public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
        if (p == null) {
            writeString(null);
            return;
        }
        //1、先写入序列化类名
        writeParcelableCreator(p);
        //2、调用类中复写的writeToParcel方法按顺序写入
        p.writeToParcel(this, parcelableFlags);
    }
 //写入序列化类名
 public final void writeParcelableCreator(@NonNull Parcelable p) {
        String name = p.getClass().getName();
        writeString(name);
    }

序列化过程中,首先写入序列化类名,然后调用类中复写的writeToParcel()方法依次写入

反序列化过程(Parcelable的读过程)

调用过程:Pacel.readValue()->readParcelable()

public final T readParcelable(@Nullable ClassLoader loader) {
//1、通过反射或缓存获取序列化类中的CREATOR
Parcelable.Creator creator = readParcelableCreator(loader);
if (creator == null) {
return null;
}
if (creator instanceof Parcelable.ClassLoaderCreator) {
Parcelable.ClassLoaderCreator classLoaderCreator =
(Parcelable.ClassLoaderCreator) creator;
return (T) classLoaderCreator.createFromParcel(this, loader);
}
//2、调用CREATOR中的createFromParcel进行反序列化
return (T) creator.createFromParcel(this);
}

private static final HashMap mCreators = new HashMap<>();

public final Parcelable.Creator readParcelableCreator(@Nullable ClassLoader loader) {
   //1、首先读取之前写入的类名
    String name = readString();
    if (name == null) {
        return null;
    }
    Parcelable.Creator creator;
    synchronized (mCreators) {
        //如果之前某个classLoader缓存过Parcelable的Creator,然后通过mCreators缓存过,
        //那么直接从缓存取;否则通过反射去加载并加入缓存中
        HashMap> map = mCreators.get(loader);
        if (map == null) {
            map = new HashMap<>();
            mCreators.put(loader, map);
        }
        creator = map.get(name);
        if (creator == null) {
            try {
                ClassLoader parcelableClassLoader =
                        (loader == null ? getClass().getClassLoader() : loader);
                  //通过反射去获取Parcelable中的CREATOR
                Class parcelableClass = Class.forName(name, false /* initialize */,
                        parcelableClassLoader);
                Field f = parcelableClass.getField("CREATOR");
                Class creatorType = f.getType();
                creator = (Parcelable.Creator) f.get(null);
            }
            map.put(name, creator);
        }
    }
    return creator;
}

Parcelable与Serializable的性能比较

Serializable

Serializable是Java中的序列化接口,其使用起来简单但开销较大(因为Serializable在序列化过程中使用了反射机制,故而会产生大量的临时变量,从而导致频繁的GC),并且在读写数据过程中,它是通过IO流的形式将数据写入到硬盘或者传输到网络上。

Parcelable

Parcelable则是以IBinder作为信息载体,在内存上开销比较小,因此在内存之间进行数据传递时,推荐使用Parcelable,而Parcelable对数据进行持久化或者网络传输时操作复杂,一般这个时候推荐使用Serializable。

在使用内存方面,Parcelable比Serializable性能高,所以推荐使用Parcelable。

Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。

Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性,在外界有变化的情况下,建议使用Serializable

参考文章:https://blog.csdn.net/u013700502/article/details/104161991

你可能感兴趣的:(Java序列化)