[Android] serialization principle Serializable

introduce

We know that when a program terminates, the objects created by this program will also terminate along with the program, so how can I not be affected by the state of other programs and can get the state of objects created by other programs? At this time, we can use Serializable for serialization to persist objects to storage devices or transmit them to other clients through the network. Let's explore how to use Serializable to complete serialization.

The concept of serialization and deserialization


The so-called serialization is to convert an object in memory into a byte stream, and deserialization is to convert a byte stream back to an object in memory.

That is to say:

       Serialization: object -> byte stream

       Deserialization: byte stream -> object

The role of serialization: persist objects, transmit data on the network, and communicate across processes

 Serializable interface


 Serializable is a serialization interface, but it is an empty interface that provides standard serialization and deserialization operations for objects. 

Define a serializable class


Just define a class, implement the Serializable interface and declare serialVersionUID (the function of this will be described below), then this class can be serialized.

class Person(val age:Int,var name:String):Serializable {
    companion object{//相当于static
        private const val serialVersionUID:Long=1L
    }
}

A class that implements serialization, and its subclasses are also serializable.

Implement serialization and save it in an array 


To achieve serialization, you need to use the methods of ObjectOutputStream and ObjectInputStream. Serialization is to use ObjectOutputStream to convert objects into byte streams, and then put byte streams into Byte arrays. Deserialization is to convert Byte arrays into objects.

//序列化的基本方法
    fun test1(){
        //序列化的过程
        var person:Person= Person(1,"xiao")
        val out = ByteArrayOutputStream()
        val oos= ObjectOutputStream(out)
        oos.writeObject(person)
        val bs=out.toByteArray()
        oos.close()
        //反序列化过程
        val ois=ObjectInputStream(ByteArrayInputStream(bs))
        val person1:Person=ois.readObject() as Person
        println("反序列化Person:person1.age=${person1.age},person1.name=${person1.name}")
    }

 Here is the result:

In fact, although the contents of the serialized person and person1 are exactly the same, person and person1 are two different objects.

Realize serialization and save in file


Use the writeObject method of ObjectOutputStream to convert the object into a byte stream and save it in the data1 file, and then convert the byte stream in the file into an object through the readObject method of ObjectInputStream.

//序列化到文件
    fun test2() {
        try {
            var person = Person(2, "xiao2",Tag("string"))
            val out=ObjectOutputStream(openFileOutput("data1",Context.MODE_PRIVATE))
            out.writeObject(person)
            out.close()
            val input=ObjectInputStream(openFileInput("data1"))
            var person1=input.readObject() as Person
            input.close()
            println("反序列化Person:person1.age=${person1.age},person1.name=${person1.name}")
        }catch (e:IOException){
            e.printStackTrace()
        }
    }

The shortcut key Ctrl+Shift+A opens the search function, enter "Device File Explorer" in the search box to find this tool, here, we find the /data/data/com.example.filepersistencetest/file/ directory, and open the The data1 file is a byte stream as shown in the figure:

 serialVersionUID


 serialVersionUID generally give it a fixed value on the line

 private const val serialVersionUID:Long=1L

The role of serialVersionUID:

The serialVersionUID is used to assist the serialization and deserialization process. In principle, the serialVersionUID in the serialized data can be deserialized normally only if it is the same as the serialVersionUID of the current class. serialVersionUID can play a role in version control.

For example: If the server is updated and the serialVersionUID is changed to 2L, then when the client is not updated, the client’s serialVersionUID is still 1L, and the client will report an error when deserializing to obtain the object on the server, then the client can only updated version.

In a serializable class, all members must implement the property status of Serializable.


Let's take an example:

Define a class Tag that does not implement the Serializable interface, and add an object tag of the Tag class to the member of Person, then this tag object is the attribute state that does not implement Serializable, and an error will appear when you run it.

data class Tag(var string:String)
class Person(val age:Int,var name:String,var tag: Tag):Serializable {//tag类没有实现Serializable接口,就会报错
    companion object{//相当于static
        private const val serialVersionUID:Long=1L
    }
}

 Well, if you make the Tag class implement the Serializable interface, it will work successfully.

data class Tag(var string:String):Serializable
class Person(val age:Int,var name:String,var tag: Tag):Serializable {//tag类没有实现Serializable接口,就会报错
    companion object{//相当于static
        private const val serialVersionUID:Long=1L
    }
}

 Someone may ask: Since it is required that all members of the serializable class implement Serializable, does that mean that the String class also implements Serializable? Yes, the String class also implements Serializable in java.

Is there a way to set a member in a serializable class not to be serialized?


1.@Transient

When a member is declared by @Transient, the default serialization mechanism will ignore this member and not serialize this member.

class Person(val age:Int, @Transient var name:String, @Transient var tag: Tag):Serializable {//tag类没有实现Serializable接口,就会报错
    companion object{//相当于static
        private const val serialVersionUID:Long=1L
    }
}

The value of member name and tag is null, it can be seen that name and tag are not serialized.

 2. Static variables will not be serialized

Objects are serialized, but static variables belong to classes, and static variables are generated in memory prior to objects.

 Let's take a look at the serialization process


writeObject->writeObject0->writeOrdinaryObject->writeSerialData() (if implementing Serializable interface) ->defaultWriteFields->writeObject0 (if writing String) ->writeString

custom serialization


There are four methods in serialization: readObject, writeObject, readResolve, writeReplace. Although there is no method for you to implement in the serialization interface, we can use these four methods as overloaded methods in the process of using serialization. What are their uses? During the serialization process, if you want to perform special processing on each field, for example, if you want to add something to the name, you can use these methods to add some operations.

class Person(var age:Int, var name:String, var tag: Tag):Serializable {//tag类没有实现Serializable接口,就会报错
    companion object{//相当于static
        private const val serialVersionUID:Long=1L
    }
    private fun readObject(inputStream: ObjectInputStream){
        println("readObject")
        age=inputStream.readObject() as Int
        name=inputStream.readObject() as String
        tag=inputStream.readObject() as Tag
    }
    private fun writeObject(outputStream:ObjectOutputStream){
        println("writeObject")
        outputStream.writeObject(age)
        outputStream.writeObject(name)
        outputStream.writeObject(tag)
    }
    private fun readResolve():Any{
       println("readResolve")
        return  Person(22,"${name} readResolve",Tag("string"))
    }
    private fun writeReplace():Any{
        println("writeReplace")
        return Person(21,"${name} writereplace",Tag("string"))
    }
}

 The running result is shown in the figure, and it can be seen that the sequence of these four methods: writeReplace, writeObject, readObject, readResolve. The final serialized object is the object returned by the readResolve method. It is obtained that the field name of the object is xiao writeReplace readResolve, indicating that the writeReplace and readResolve methods are called successively during the serialization process.

 Obviously we are calling writeObject of ObjectOutputStream, why is the method defined in the class automatically called? In fact, in the writeObject method, it will check whether you have written the writeObject method in the class. If you have written it, it will write by calling the writeObject in the class through reflection instead of calling the default method.

singleton object problem


Singleton object a->after serialization->serialized out->singleton object b

Are a and b still the same singleton object? It's not the same anymore.

How to make a and b the same singleton object?

When deserializing, return this singleton directly, so as to ensure that this singleton is still obtained when the singleton is serialized.

class Single:Serializable{
    companion object{
        private  const val serialVersionUID:Long=1L
        private var flag=false
        var single:Single?=null
        @Synchronized fun getInstance():Single{
            if(single==null){
                single= Single()
            }
            return single!!
        }
    }
    init {
        if(single==null){
            synchronized(Single::class){
                if(!flag){
                    flag=true
                }
                else{
                    throw RuntimeException("单例模式被侵犯")
                }
            }
        }
    }
    //解决单例序列化的问题
    private  fun readResolve():Any{
        println("readResolve")
        //反序列化的时候,直接把这个单例返回,这样保证单例被序列化时得到的还是这个单例
        return  single!!
    }
}

 Next we look at the Externalizable interface


The Externalizable interface implements the Serializable interface, and there are two methods that we need to rewrite. One is the writeExternal method, how to write the object into the serialization, and the other is the readExternal method, how to read the object from the serialization.

Define a class that implements the Externalizable interface

class Person2:Externalizable{
    lateinit var name:String
    var age:Int=0
    //需要一个无参的构造方法
    constructor()
    constructor(age:Int,name: String):this(){
        this.age=age
        this.name=name
    }
    companion object{//相当于static
        private const val serialVersionUID:Long=1L
    }
    override fun writeExternal(p0: ObjectOutput?) {
        println("writeExternal")
        if (p0 != null) {
            p0.writeObject(name)
            p0.writeObject(age)
        }
    }
    override fun readExternal(p0: ObjectInput?) {
        println("readExternal")
        if (p0!=null){
            name=p0.readObject() as String
            age=p0.readObject() as Int
        }
    }
}

reference:

Station B: jaryjun: basic concept of serialization 01-Golden Lion.___1

                        Serializable principle and interview point 02-Golden Lion.___1

Book: Exploring the Art of Android Development

 

 

 
 
 
 

 
 

 
 

 

Guess you like

Origin blog.csdn.net/weixin_63357306/article/details/128518856