你真的会使用android序列化吗?
目录
1.Serializable接口。
Serializable接口的java提供的一个序列化接口。他可以实现序列化和反序列化。字需要在接口中实现就好,但是初次使用的时候很容易掉坑。就是没有标明serialVersionUID,private static final long serialVersionUID = 1L;。为什么要标明这个我们下面举例说明。
一般我们在使用的读写的时候都会先把对象序列化。先创建一个User类,并且实现一个Serializable接口。
User类
public class User implements Serializable { private int usetid; private String userName; private boolean isMale; private int age; public User() { } public User(int usetid, String userName, boolean isMale) { this.usetid = usetid; this.userName = userName; this.isMale = isMale; } public User(int usetid, String userName, boolean isMale, int age) { this.usetid = usetid; this.userName = userName; this.isMale = isMale; this.age = age; } @Override public String toString() { return "User{" + "usetid=" + usetid + ", userName='" + userName + '\'' + ", isMale=" + isMale + ", age=" + age + '}'; } }
接下来我们通过写入、读取本地文件来实现序列化反序列化。
序列化
User user = new User(2, s, true);
out = new ObjectOutputStream(new FileOutputStream(FEED_DIR));
out.writeObject(user);
out.close();
反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream(FEED_DIR));
User user = (User) in.readObject();
in.close();
完整代码:
public class MainActivity extends AppCompatActivity {
private Button button1;
private Button button2;
private TextView tv;
private EditText et_num;
private String FEED_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyCache/" + "cache.txt";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initView() {
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
tv = findViewById(R.id.tv);
et_num = findViewById(R.id.et_num);
}
private void initData() {
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//todo:创建文件以及从中取值
File feedTypeFile = new File(FEED_DIR);
//检测sd卡是否安装
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//文件不存在
if (!feedTypeFile.exists()) {
try {
//先创建文件夹/目录
feedTypeFile.getParentFile().mkdirs();
//再创建新文件
feedTypeFile.createNewFile();
} catch (Exception e) {
e.printStackTrace();
tv.setText(e.toString());
}
}
String s = et_num.getText().toString();
User user = new User(2, s, true);
ObjectOutputStream out = null;
try {
out = new ObjectOutputStream(new FileOutputStream(FEED_DIR));
out.writeObject(user);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
tv.setText("没有内存卡");
}
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(FEED_DIR));
User user = (User) in.readObject();
in.close();
tv.setText(user.toString());
} catch (Exception e) {
e.printStackTrace();
tv.setText(e.toString());
}
}
});
}
}
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="序列化" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="反序列化" />
<EditText
android:id="@+id/et_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="输入名称" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
这样做很简单就实现了序列化,但是因为没有声明serialVersionUID会有想不到的结果。我们继续
首先我们在user类里添加一个age属性
public class User implements Serializable {
private int usetid;
private String userName;
private boolean isMale;
private int age;
public User() {
}
public User(int usetid, String userName, boolean isMale) {
this.usetid = usetid;
this.userName = userName;
this.isMale = isMale;
}
public User(int usetid, String userName, boolean isMale, int age) {
this.usetid = usetid;
this.userName = userName;
this.isMale = isMale;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"usetid=" + usetid +
", userName='" + userName + '\'' +
", isMale=" + isMale +
", age=" + age +
'}';
}
}
然后运行项目点击“反序列化”
报一个错
11-07 16:51:46.625 3664-3664/com.example.administrator.aidlsend W/System.err: java.io.InvalidClassException: com.example.administrator.aidlsend.User; Incompatible class (SUID): com.example.administrator.aidlsend.User: static final long serialVersionUID =6259768693016371241L; but expected com.example.administrator.aidlsend.User: static final long serialVersionUID =-9121888606695983862L;
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at java.io.ObjectInputStream.verifyAndInit(ObjectInputStream.java:2341)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at java.io.ObjectInputStream.readNewClassDesc(ObjectInputStream.java:1643)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:657)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1782)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:761)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1983)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1940)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at com.example.administrator.aidlsend.MainActivity$2.onClick(MainActivity.java:87)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at android.view.View.performClick(View.java:4780)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at android.view.View$PerformClick.run(View.java:19866)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at android.os.Handler.handleCallback(Handler.java:739)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at android.os.Handler.dispatchMessage(Handler.java:95)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at android.os.Looper.loop(Looper.java:135)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5254)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at java.lang.reflect.Method.invoke(Native Method)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at java.lang.reflect.Method.invoke(Method.java:372)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)
11-07 16:51:49.668 1457-1629/? D/ld_audio_hw: lineFun, out_standby
意思就是说序列化和反序列化的serialVersionUID不一致,发反序列化失败。
原因就是当我们在没有设置的时候,系统会计算一个对应的hash值付给serialVersionUID,当我们改变属性的时候,系统会再次计算并把hash值付给serialVersionUID,所以两次的UID不一致自然无法反序列化。所以我们需要手动声明一个serialVersionUID,这个值可以是自己定义的比如:1L。也可以是系统计算的hash值。
那么每次使用Seriailzable的时候忘记添加,或者添加重复怎么办,如何获取系统计算的hash值呢?
自动检测serialVersionUID
之后就会有提示了
然后Alt+Enter 提示添加
结果:
private static final long serialVersionUID = 2602503210684455922L;
2.parcelable
parcelable是android为我们提供的一个实现序列化的接口,实现这个类就给可以通过Inten和Binder传递数据。但是相对于Serializable来说,在代码量上来说他更加的复杂一些。
public class Persion implements Parcelable{
private String name;
private String id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Persion(String name, String id) {
this.name = name;
this.id = id;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeString(this.id);
}
public Persion() {
}
protected Persion(Parcel in) {
this.name = in.readString();
this.id = in.readString();
}
public static final Creator<Persion> CREATOR = new Creator<Persion>() {
@Override
public Persion createFromParcel(Parcel source) {
return new Persion(source);
}
@Override
public Persion[] newArray(int size) {
return new Persion[size];
}
};
}
增加了这么几个方法
@Override
public int describeContents() {
return 0;
}
describeContents() 功能:返回当前对象的内容描述,如果含有文件描述符返回1,否则返回0,几乎所有情况返回0.
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeString(this.id);
}
writeToParcel(Parcel dest,int flags) 功能:将当前对象写入序列化结构中,其中flags标识有两种值:0或1.为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0
protected Persion(Parcel in) {
this.name = in.readString();
this.id = in.readString();
}
persion(Parcel in)工能:从序列化后的对象中创建原始的对象。
public static final Creator<Persion> CREATOR = new Creator<Persion>() {
@Override
public Persion createFromParcel(Parcel source) {
return new Persion(source);
}
@Override
public Persion[] newArray(int size) {
return new Persion[size];
}
};
createFromParcel(Parcel source) 功能:从序列化后的对象中创建原始对象。
newArray(int size) 功能:创建指定长度的原始对象数组。
以上可以看到各种Parcel。那个这个Parcel是做什么的呢?
里面的这个Parcel,是内部包装了可序列化的数据,可以在Binder中只有的传输。在序列化过程中需要实现的功能有序列化,反序列化和内容描述。其中序列化功能由writeToParcel方法完成,最终是通过Parcel中的一系列write方法来完成的。反序列化由CREATOR来完成,其内部标明了如何创建序列化和数组,并通过Parcel中一些列read方法来完成反序列化的过程。内容描述功能由describeContents方法来完成。几乎在所有情况下这个方法的返回值都为0,仅在当前对象中存在文件描述符时,此方法返回1.
那么每次都要手写这么多代码吗?当然不用,下面介绍如何快速实现这些代码。
自动生成Parcelable相关代码
首先 Settings ->Plugins->Borwse repositories->搜索 Parcelabe->安装->重启
使用:Alt+Insert
点击OK 就可以了
3.Parcelable和Serializable的优缺点
Serializable是java中的序列化接口。使用简单但是开销大,序列化与反序列化需要大量的I/O操作。
Parcelable是Android中的序列化接口。更加适合在android平台使用,使用起来复杂一下,但是效率高。
使用:Parcelable主要用在内存上的序列化
Serializable适合将对象储存在设备中,和将对象序列化后通过网络传输的情况下使用