Analysis of Parcelable and Serializable of Serialization and Deserialization

In daily application development, we may need to let some objects leave the memory space and store them on physical disks for long-term preservation, while also reducing the pressure on memory, and reading them from disk to memory when needed, For example, if a specific object is saved in a file, and then read into memory for use after a period of time, the object needs to implement serialization operation. In java, the Serializable interface can be used to realize the serialization of the object, and In android, either the Serializable interface can be used to implement object serialization or the Parcelable interface can be used to implement object serialization, but it is more inclined to implement the Parcelable interface during memory operations, which will make the use of transmission more efficient. Next, we will introduce these two serialization operations in detail respectively ~~~~.

Serialize and deserialize

First, let's understand serialization and deserialization.

  • Serialization
    Since objects in memory are temporary and cannot be stored for a long time, in order to maintain the state of the object, it is necessary to write the object to disk or other media at this time. This process is called serialization.
  • Deserialization
    Deserialization is just the reverse operation of serialization, that is, to deserialize (read) objects that already exist on disk or other media into memory for subsequent operations, and this process is It's called deserialization.

      In a nutshell, serialization refers to the process of storing the state of an object instance to a storage medium (disk or other medium). In this process, the public and private fields of the object and the name of the class (including the assembly where the class is located) are converted into a byte stream, and then the byte stream is written to the data stream. When the object is subsequently deserialized, an exact copy of the original is created.

  • Necessary conditions for serialization To achieve serialization,
    an object must implement the Serializable interface or the Parcelable interface. The Serializable interface is a serialization abstract class in java, and the Parcelable interface is a unique serialization in android. Interfaces, in some cases, the serialization implemented by the Parcelable interface is more efficient. We will analyze their implementation cases in the future, as long as it is clear that one of the Serializable interface or the Parcelable interface must be implemented when implementing the serialization operation.
  • The application scenarios of serialization
    mainly include the following situations (but not limited to the following situations):
    1) The object in the memory is written to the hard disk;
    2) The object is transmitted on the network by the socket;
    3) The remote method call through RMI (Remote Method Invoke) ) transfer object;

Next, we introduce the two implementations of serialization.

Serializable

Serializable is a serialization interface provided by java. It is an empty interface that provides standard serialization and deserialization operations for objects. It is relatively simple to use Serializable to implement class serialization, as long as the Serializable interface is implemented in the class declaration. , while declaring a serialized identity is strongly recommended. as follows:

package com.zejian.ipctest;

import java.io.Serializable;

public class Client implements Serializable{

    /**
     * 生成序列号标识
     */
    private static final long serialVersionUID = -2083503801443301445L;

    private int id;
    private String name;


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

As shown in the above code, the Serializable interface implemented by the Client class also declares the serialization identifier serialVersionUID, which is generated by the editor and can of course be customized, such as 1L, 5L, but it is recommended to use the editor to generate a unique identifier. So what does serialVersionUID do? In fact, it is okay for us not to declare serialVersionUID, because a serialVersionUID is automatically generated during serialization to identify the serialized object. In this case, do we still need to specify? Since the serialVersionUID is used to assist the serialization and deserialization process, in principle, the serialVersionUID in the serialized object can be deserialized normally only if it is the same as the serialVersionUID of the current class, that is, the serialVersionUID of serialization and deserialization must be the same for the serialization operation to succeed. The specific process is as follows: during serialization, the system will write the serialVersionUID of the current class into the serialized file. When deserializing, the system will detect the serialVersionUID in the file to determine whether it is consistent with the serialVersionUID of the current class. If it is consistent, it means that the version of the serialized class is the same as the current class version, and the deserialization can be successful, otherwise it fails. The following UID error is reported:

Exception in thread "main" java.io.InvalidClassException: com.zejian.test.Client; 
local class incompatible: stream classdesc serialVersionUID = -2083503801443301445, 
local class serialVersionUID = -4083503801443301445

Therefore, it is strongly recommended to specify serialVersionUID, so that even a small change will not cause a crash. If it is not specified, as long as there is one more space in the file, the UID automatically generated by the system will be completely different, and the deserialization will fail. ok~, to understand so much, let's take a look at an example of how to serialize and deserialize objects:

package com.zejian.ipctest;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;


public class Stest {

    public static void main(String[] args) throws Exception {

        //把对象序列化到文件
        Client client = new Client();
        client.setId(000001);
        client.setName("client");

        ObjectOutputStream oo = new ObjectOutputStream
                (new FileOutputStream("/Users/Desktop/cache.txt"));
        oo.writeObject(client);
        oo.close();

        //反序列化到内存
        ObjectInputStream oi = new ObjectInputStream
            (new FileInputStream("/Users/Desktop/cache.txt"));
        Client c_back = (Client) oi.readObject();
        System.out.println("Hi, My name is " + c_back.getName());
        oi.close();

    }
}

It can be seen from the code that only ObjectOutputStream and ObjectInputStream can realize the serialization and deserialization of objects, write the client object to the file through the stream object, and restore the c_back object when needed, but the two are not the same object Now, the deserialized object is newly created. There are two special points to note here. If the type or class name of the member variable of the deserialized class changes, even if the serialVersionUID is the same, the deserialization will not be successful. Secondly, static member variables belong to classes and not objects, and will not participate in the serialization process, and member variables marked with the transient keyword will not participate in the serialization process.
  The keyword transient, briefly explained here, Java's serialization provides a mechanism for persisting object instances. When persisting objects, there may be a special object data member that we don't want to use the serialization mechanism to save. To turn off serialization on a field of a particular object, the keyword transient can be prefixed to the field. When an object is serialized, the values ​​of transient variables are not included in the serialized representation, whereas non-transient variables are included.
  In addition, the default serialization process of the system can be changed. By implementing the following four methods, the default serialization and deserialization processes of the system can be controlled:
  

package com.zejian.ipctest;

import java.io.IOException;
import java.io.ObjectStreamException;
import java.io.Serializable;


public class Client implements Serializable{

    /**
     * 生成序列号标识
     */
    private static final long serialVersionUID = -4083503801443301445L;


    private int id;
    private String name;


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    /**
     * 序列化时,
     * 首先系统会先调用writeReplace方法,在这个阶段,
     * 可以进行自己操作,将需要进行序列化的对象换成我们指定的对象.
     * 一般很少重写该方法
     * @return
     * @throws ObjectStreamException
     */
    private Object writeReplace() throws ObjectStreamException {
        System.out.println("writeReplace invoked");
        return this;
    }
    /**
     *接着系统将调用writeObject方法,
     * 来将对象中的属性一个个进行序列化,
     * 我们可以在这个方法中控制住哪些属性需要序列化.
     * 这里只序列化name属性
     * @param out
     * @throws IOException
     */
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        System.out.println("writeObject invoked");
        out.writeObject(this.name == null ? "zejian" : this.name);
    }

    /**
     * 反序列化时,系统会调用readObject方法,将我们刚刚在writeObject方法序列化好的属性,
     * 反序列化回来.然后通过readResolve方法,我们也可以指定系统返回给我们特定的对象
     * 可以不是writeReplace序列化时的对象,可以指定其他对象.
     * @param in
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private void readObject(java.io.ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        System.out.println("readObject invoked");
        this.name = (String) in.readObject();
        System.out.println("got name:" + name);
    }


    /**
     * 通过readResolve方法,我们也可以指定系统返回给我们特定的对象
     * 可以不是writeReplace序列化时的对象,可以指定其他对象.
     * 一般很少重写该方法
     * @return
     * @throws ObjectStreamException
     */
    private Object readResolve() throws ObjectStreamException {
        System.out.println("readResolve invoked");
        return this;
    }
}

Through the above 4 methods, we can control the serialization process at will. Since we do not need to rewrite these 4 methods in most cases, we only introduce them here, as long as we know that there is such a thing Just do it. ok~, the introduction to Serializable is here first.

Parcelable

In view of the fact that Serializable has a large overhead in memory serialization, and memory resources are rare resources in the android system (the memory overhead allocated by the android system to each application is limited), the Parcelable interface is provided in android to realize serialization. Operation, Parcelable has better performance than Serializable, and it has less memory overhead. Therefore, it is recommended to use Parcelable when transferring data between memories, such as transferring data between activities through Intent. The disadvantage of Parcelable is that it is more troublesome to use. The following is a The implementation case of the Parcelable interface, let's feel it:

package com.zejian.ipctest;
import android.os.Parcel;
import android.os.Parcelable;

public class NewClient implements Parcelable {

    public int id;
    public String name;
    public User user;

    /**
     * 当前对象的内容描述,一般返回0即可
     * @return
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * 将当前对象写入序列化结构中
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.id);
        dest.writeString(this.name);
        dest.writeParcelable(this.user,0);
    }

    public NewClient() {
    }

    /**
     * 从序列化后的对象中创建原始对象
     * @param in
     */
    protected NewClient(Parcel in) {
        this.id = in.readInt();
        this.name = in.readString();
       //User是另一个序列化对象,此方法序列需要传递当前线程的上下文类加载器,否则会报无法找到类的错误
       this.user=in.readParcelable(Thread.currentThread().getContextClassLoader());
    }

    /**
     * public static final一个都不能少,内部对象CREATOR的名称也不能改变,必须全部大写。
     * 重写接口中的两个方法:
     * createFromParcel(Parcel in) 实现从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层,
     * newArray(int size) 创建一个类型为T,长度为size的数组,供外部类反序列化本类数组使用。
     */
    public static final Parcelable.Creator<NewClient> CREATOR = new Parcelable.Creator<NewClient>() {
        /**
         * 从序列化后的对象中创建原始对象
         */
        @Override
        public NewClient createFromParcel(Parcel source) {
            return new NewClient(source);
        }

        /**
         * 创建指定长度的原始对象数组
         * @param size
         * @return
         */
        @Override
        public NewClient[] newArray(int size) {
            return new NewClient[size];
        }
    };
}

As can be seen from the code, the functions that need to be implemented in the serialization process include serialization, deserialization, and content description. Among them, the writeToParcel method realizes the serialization function, which is completed internally through a series of write methods of Parcel, and then deserializes through the CREATOR internal object, which internally creates the serialized object through the createFromParcel method and creates an array through the newArray method. Finally, a series of read methods of Parcel are used to complete deserialization, and finally describeContents completes the content description function. This method generally returns 0, and returns 1 only when there is a file descriptor in the object. At the same time, since User is another serialized object, the context class loader of the current thread needs to be passed in the deserialization method, otherwise an error that the class cannot be found will be reported.
  To sum it up in one sentence, we map our objects to Parcel objects through writeToParcel, and then map the Parcel objects to our objects through createFromParcel. Parcel can also be regarded as a read and write stream similar to Serliazable. Objects are written to the stream through writeToParcel, and objects are read from the stream through createFromParcel. This process needs to be implemented by ourselves and the order of writing and reading must be Consistent. ok~, the serialization implementation of the Parcelable interface has been basically introduced.
  So where would the Parcelable object be used? In fact, when passing data of complex types (such as custom reference type data) through Intent, you need to use Parcelable objects. The following are some operation methods of Intent on Parcelable objects in daily applications. Reference types must implement the Parcelable interface to be passed through Intent, and basic The data type, the String type, can be passed directly through the Intent and the Intent itself also implements the Parcelable interface, so it can be easily transferred between components.
  write picture description here
  
  In addition to the above Intent, the system also provides us with other classes that implement the Parcelable interface, such as Bundle and Bitmap, which can be directly serialized, so we can easily use them to transfer data between components. Of course, the Bundle itself It is also a container similar to key-value pairs, and can also store Parcelable implementation classes. Its API methods are basically similar to Intent. Since these are basic knowledge points of android, we will not introduce them too much here.

Difference between Parcelable and Serializable

  • The implementation difference between the two is the implementation
      of Serializable, only need to implement the Serializable interface. This just puts a tag (UID) on the object and the system automatically serializes it. The implementation of Parcelabel not only needs to implement the Parcelabel interface, but also needs to add a static member variable CREATOR to the class. This variable needs to implement the Parcelable.Creator interface and implement the abstract method of reading and writing.
      
  • The original intention of the design of the two The original design intention
      of Serializable is to serialize objects to local files, databases, network streams, and RMI for data transmission. Of course, this transmission can be within a program or between two programs. The original design of Android's Parcelable is due to the low efficiency and high consumption of Serializable, and the data transfer in android is mainly in the memory environment (memory is a rare resource in android), so the appearance of Parcelable is to meet the low overhead of data in memory And pass the problem efficiently.
      
  • The
      performance of Parcelable is better than that of Serializable, and the memory overhead is smaller. Therefore, it is recommended to use Parcelable when transferring data between memory in Android applications, such as data transfer between activities and AIDL data transfer, while Serializable persists data. It is convenient, so it is recommended to choose Serializable when serializing objects into storage settings or serializing objects through the network (Parcelable is also possible, but the implementation and operation process is too cumbersome and in order to prevent different versions of android, the Parcelable may be different. Therefore, try to choose the Serializable interface when serializing to storage devices or network transmission).
      
  • The common point between the two should be noted that
      whether it is Parcelable or Serializable, the object after the deserialization operation is newly created, which is not the same as the original object, but the content is the same.

Shortcut generation in Android studio

  • Android studio quickly generates Parcelable code

    In the process of program development, the code we implement the Parcelable interface is similar. If we implement a Parcelable interface class every time, we have to write repeated code, which is obviously not desirable, but fortunately, android studio Provides a plug-in that automatically implements the method of the Parcelable interface. It is quite implemented. We only need to open the Setting, find the plugin plug-in, then search for the Parcelable plug-in, and finally find the android Parcelable code generator. Install it:
    write picture description here
    after restarting android studio, we create a User class, As follows:
    write picture description here
    Then use the plug-in just installed to help us generate the code to implement the Parcelable interface, window shortcut: Alt+Insert, Mac shortcut: cmd+n, as follows:
    write picture description here
    The final result is as follows:

/**
 * 作者:Mr_Han on 2018/4/23 16:11
 * 邮箱:[email protected]
 */

public class UserBean implements Parcelable {
    private String name;
    private int age;

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

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

    public UserBean() {
    }

    protected UserBean(Parcel in) {
        this.name = in.readString();
        this.age = in.readInt();
    }

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

        @Override
        public UserBean[] newArray(int size) {
            return new UserBean[size];
        }
    };
}
  • Android studio quickly generates Serializable UID
      Under normal circumstances, AS is by default to turn off the serialVersionUID generation prompt, we need to open the setting, find the detection (Inspections option), and enable the Serializable class without serialVersionUID detection, as follows:
    write picture description here
     Then create a new User class to implement Serializable Interface, the right side will prompt to add serialVersionUID, as follows:
    write picture description here
     Finally, on the class name, Alt+Enter (Mac:cmd+Enter), shortcut code prompt, generate serialVersionUID: the
    write picture description here
     final result is as follows:
    write picture description here
     ok~, the above are Parcelable and Serializable The entire content of the interface, the end of this article.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324697657&siteId=291194637