Android中的Serializable和Parcelable

1 篇文章 0 订阅
1 篇文章 0 订阅

本篇有部分内容: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时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0PARCELABLE_WRITE_RETURN_VALUE
describeContents返回当前对象的内容描述。如果含有文件描述符,返回1(见右侧),否则返回0,几乎所有情况都返回0CONTENTS_FILES_DESCRIPTOR

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

Parcelable与Serializable区别

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值