最近在学习Rxjava相关的内容,从github上clone别人写项目来学习,发现Parcelable这个接口,google了一下,发现这个东西还挺重要的,于是决定巴拉巴拉,加深一下自己的认识
1.什么是序列化和反序列化
- 序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。这个过程逆过来就是反序列化了。
序列化和反序列化的关系: - 序列化目的:
a、以某种存储形式使自定义对象持久化;
b、将对象从一个地方传递到另一个地方。
c、使程序更具维护性。
举个例子:
经过多年的艰苦努力,地球上的科学家开发了一种机器人可以帮助他们在日常工作中。但是,这种机器人比火星的科学家研制机器人的功能要少。
这两个行星的科学家之间的一次会议后,决定火星将他们的机器人送到地球。但问题的发生。发送100机器人地球的费用是$ 100亿美元。它需要60天左右的旅行。
最后,三月的科学家们决定将分享他们的地球科学家的秘密。这个秘密是关于类/机器人的结构。地球科学家开发地球本身上相同的结构。三月的科学家连载每个机器人的数据并将其发送到地球。地球上的科学家们反序列化数据,并相应馈入每个机器人。
这个过程中进行数据通信的巨量省了他们的时间和金钱。
简单来说就是序列化使得数据的传输变得更加高效!
2.序列化通常实现方法
- 实现Serializable接口(这是Java自带的)
- 实现parcelable接口(这是Android特有属性)
3. Serializable
- 实现Serializable接口
package gpwner;
import java.io.Serializable;
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
private String name;
public Student(int id, String name) {
super();
this.id = id;
this.name = 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;
}
}
- 序列化
package gpwner;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Persist {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
Student student=new Student(2014, "Gpwner");
FileOutputStream fileOutputStream=new FileOutputStream("file.db");
ObjectOutputStream objectOutputStream=new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(student);
objectOutputStream.flush();
System.out.println("序列化成功");
}
}
运行结果:
- 反序列化
package gpwner;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Depersist {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream=new FileInputStream("file.db");
@SuppressWarnings("resource")
ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
Student student=(Student) objectInputStream.readObject();
System.out.println("反序列化成功");
System.out.println("id: "+student.id+" name: "+student.getName());
}
}
运行结果:
**这里有一个需要注意的地方**Student类中有一个静态成员变量
private static final long serialVersionUID = 1L;
他有什么用呢?
我们在序列化之前的serialVersionUID = 1L;
序列完成之后,我把serialVersionUID改成5 serialVersionUID = 5;
接下来就抛出局部类不相容的异常了:
Exception in thread "main" java.io.InvalidClassException: gpwner.Student; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 5
at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
at java.io.ObjectInputStream.readClassDesc(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at gpwner.Depersist.main(Depersist.java:13)
简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)。
如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,未作更改的类,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。
如果你没有考虑到兼容性问题时,就把它关掉,不过有这个功能是好的,只要任何类别实现了Serializable这个接口的话,如果没有加入 serialVersionUID,Eclipse都会给你warning提示,这个serialVersionUID为了让该类别 Serializable向后兼容。
4.parcelable接口
- 实现parcelable接口
package com.example.gpwner.myapplication;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by Gpwner on 2016/10/29.
*/
public class Student implements Parcelable {
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;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.id);
dest.writeString(this.name);
}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
protected Student(Parcel in) {
this.id = in.readInt();
this.name = in.readString();
}
public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() {
@Override
public Student createFromParcel(Parcel source) {
return new Student(source);
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
}
第一个Activity:
package com.example.gpwner.myapplication;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Student student=new Student(2014,"Gpwner");
Bundle bundle=new Bundle();
bundle.putParcelable("student",student);
Intent intent=new Intent(MainActivity.this,Main2Activity.class);
intent.putExtras(bundle);
startActivity(intent);
}
});
}
}
Main fest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.gpwner.myapplication">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Main2Activity"
android:label="@string/title_activity_main2"
android:theme="@style/AppTheme.NoActionBar"></activity>
</application>
</manifest>
第二个Activity:
package com.example.gpwner.myapplication;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.TextView;
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Intent intent=getIntent();
Bundle bundle=intent.getExtras();
Student student=bundle.getParcelable("student");
((TextView)findViewById(R.id.textView2)).setText("学号: "+student.getId()+" 姓名:"+student.getName());
}
}
想一想这跟普通的Activity之间的通信有什么不同??(当然是效率得到提高了)
在这里介绍AndroidStudio中一个实现parcelable的高效工具:
安装教程:
- 打开AndroidStudio->settings->plugin,在搜索框中输入“parcelable”
- 点击Browse repositories
- install->restart androidstudio
插件安装完毕了
使用教程:
- 新建student类
public class Student {
private int id;
private String name;
}
- 鼠标右键generate
- Parcelable
你已经实现了Parcelable
5. Serializable和parcelable的区别与联系
- 两者的实现差异
Serializable的实现,只需要实现Serializable接口即可。这只是给对象打了一个标记(UID),系统会自动将其序列化。而Parcelabel的实现,不仅需要实现Parcelabel接口,还需要在类中添加一个静态成员变量CREATOR,这个变量需要实现 Parcelable.Creator 接口,并实现读写的抽象方法。 - 两者效率选择
Parcelable的性能比Serializable好,在内存开销方面较小,所以Android应用程序在内存间数据传输时推荐使用Parcelable,如activity间传输数据和AIDL数据传递,而Serializable将数据持久化的操作方便,因此在将对象序列化到存储设置中或将对象序列化后通过网络传输时建议选择Serializable(Parcelable也是可以,只不过实现和操作过程过于麻烦并且为了防止android版本不同而导致Parcelable可能不同的情况,因此在序列化到存储设备或者网络传输方面还是尽量选择Serializable接口)。 - 两者需要注意的共同点
无论是Parcelable还是Serializable,执行反序列操作后的对象都是新创建的,与原来的对象并不相同,只不过内容一样罢了。
最后需要看源码,请戳
Github https://github.com/Gpwner/Serializable_parcelable