Android中的Serializable和Parcelable

来源:互联网 发布:泰牛程序员学费多少 编辑:程序博客网 时间:2024/06/02 03:07

本篇有部分内容:1、Android studio 中设置自动生成serialVersionUID。2、serialVersionUID作用 3、Parcelable

上篇文章我们知道了如何开启一个线程,也知道了多进程会导致的问题。不过不用担心,系统提供了很多跨进程通信的方法,虽说还是不能直接共享内存,但是可以通过跨进程通信还是可以实现数据交互。
方式很多:通过Intent传递数据,共享文件和SharedPreference(并发有问题),基于Binder的AIDL和Socket等。

Serializable和Parcelable统称为序列化,他们作用是将对象转换成可存储和传输的状态。例如:保存到本地文件,网络传输时,甚至两个Activity传输数据时。

先说Serializable

Android studio serialVersionUID生成

Serializable是Java提供的一个序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操作。使用Serializable实现序列化很简单,只要实现这个接口,并在其中添加一个标识:

private static final long serialVersionUID = -765267859799146771L;

这个标识可以自动生成,也可以直接自己成一个值,如,1L什么的。
有人说,我的studio为毛不能自动生成 serialVersionUID 啊,别急,此处默认不生成也没有提示,需要我们设置一下。
步骤:
1. AS->Settings->Inspections->Java->Serializations issues->Serializable class without ‘serialVersionUID’ 勾选中,apply即可。
serialVersionUID自动生成
2. 然后,选中implements Serializable的类名,Alt+Enter,根据提示操作。
Alt+Enter

序列化操作

下面的例子中会使用到getDir()等,关于几种Android中获取路径方法,可参考Android存储使用参考
User类

public class User implements Serializable{    private int age;    private String name;    public User(int age, String name) {        this.age = age;        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

MainActivity,两个button,一个textview。

/** * 序列化 */public void save(View view) {    File externalFilesDir = getExternalFilesDir("");    File file = new File(externalFilesDir, "test.txt");    if (!file.exists()) {        try {            file.createNewFile();        } catch (IOException e) {            e.printStackTrace();        }    }    try {        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));        objectOutputStream.writeObject(new User(18, "Lily"));        objectOutputStream.close();        Log.d(TAG, "save: 序列化完毕!");    } catch (IOException e) {        e.printStackTrace();    }}/** * 反序列化 */public void restore(View view) {    File file = new File(getExternalFilesDir(""), "test.txt");    try {        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));        User user = (User) objectInputStream.readObject();        textView.setText(user.getName() + "\t" + user.getAge());        objectInputStream.close();    } catch (IOException | ClassNotFoundException e) {        e.printStackTrace();    }}

操作及结果
序列化操作

serialVersionUID作用

有人说了,我擦,你上面serialVersionUID什么都没写。
别慌,这时候没什么错。我们稍微改动一下上面的User,添加一个isMale。这时我们再去点击反序列化(本初不点击“序列化”,本地已经存储)

public class User implements Serializable{    private int age;    private String name;    private boolean isMale;    ******}

会报一个错误

java.io.InvalidClassException: com.breezehan.serializable.User; local class incompatible: stream classdesc serialVersionUID = -7507481965945576883, local class serialVersionUID = -9068471045426611656

就是,两者的serialVersionUID 的不对照,没法儿反序列化。
这里的serialVersionUID ,是程序根据当前类的结构(变量、方法等),计算出来的一个hash值。

想看serialVersionUID 的作用,我们添加serialVersionUID = 1L,或者自动生成,进行序列化,然后添加isMale,进行反序列化,你会发现,这次会成功的。

serialVersionUID 工作机制(摘自Android开发艺术探索)

序列化的时候系统会把当前类的serialVersionUID 写入序列化的文件中(也可能是其他中介),当反序列化的时候系统会去检测文件中的serialVersionUID ,看是否和当前类的serialVersionUID 一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变换,比如成员变量的数量、类型可能发生了改变,这个时候是无法正常反序列的。

我们添加了serialVersionUID ,也只能说最大程度上避免反序列化失败。比如,我们可能删除或增加了一些成员变量,这个时候可以成功;但是当类结构发生了非常规性的改变,比如修改了类名,变量类型,这个时候仍然会失败。

Parcelable实现

public class People implements Parcelable {    private int age;    private String name;    private boolean isMale;    private Book book;    protected People(Parcel in) {        age = in.readInt();        name = in.readString();        isMale = in.readByte() != 0;        book = in.readParcelable(Book.class.getClassLoader());    }    public static final Creator<People> CREATOR = new Creator<People>() {        @Override        public People createFromParcel(Parcel in) {            return new People(in);        }        @Override        public People[] newArray(int size) {            return new People[size];        }    };    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeInt(age);        dest.writeString(name);        dest.writeByte((byte) (isMale ? 1 : 0));        dest.writeParcelable(book, flags);    }}

看起来挺复杂,但是其实studio都会自动帮我们生成。

Android开发艺术探索中注释

方法 功能 标记位 createFromParcel(Parcel in) 从序列化的对象中创建原始对象 newArray(int size) 创建指定长度的原始对象数组 People(Parcel in) 从序列化后的对象中创建原始对象 writeToParcel(Parcel dest, int flags) 将当前对象写入序列化结构中,其中flags标识有两种值:0或者1(见右侧)。为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0 PARCELABLE_WRITE_RETURN_VALUE describeContents 返回当前对象的内容描述。如果含有文件描述符,返回1(见右侧),否则返回0,几乎所有情况都返回0 CONTENTS_FILES_DESCRIPTOR

然后我们就可以在intent和binder中使用了。其中writeToParcel是序列化,反序列化是CREATOR,最终通过Parcel 的一系列read完成。

Parcelable与Serializable区别

Serializable是java中的方法,使用简单但是开销大,有大量I/O操作。Parcelable是Android特有的序列化方法,性能较高。
在内存中使用时,建议Parcelable;存储或网络传输时建议Serializable(Parcelable也能实现,较复杂)。

0 0
原创粉丝点击