Serialiable和Parcelable的用法和区别

在Android中,经常会遇到这样的情况,如何对两个Activity之间传递消息,熟悉Android开发的,肯定知道用Intent,对于自定义的Object,我们会使用Bundle中的putSerializable(),或者Bundle.putParcelable(),可是我比较喜欢用Serialiable的方式,因为简答啊,代码少啊,可是在Android中进程间通信里,我们要传递一个自定义的Object,需要使用Parceable的方式,所以得搞搞它们。

Java中的序列化

在Java 1.1 增添了一种特性,叫“对象序列化”(Object Serialization)。它面向那些实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复原来的样子。这一过程可通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。

对象序列化不仅保存了对象的“全景图”,而且能追踪对象内包含的所有句柄并保存那些对象,接着又能对每个对象内包含的句柄进行追踪,以此类推。

先看一个例子:

class Data implements Serializable{
    private int i;
    Data(int x){
        i = x;
    }

    @Override
    public String toString() {
        return Integer.toString(i);
    }
}

public class Worm implements Serializable {

    private static int r(){
        return (int)(Math.random()*10);
    }

    private Data[] d = {
            new Data(r()),new Data(r()),new Data(r())
    };

    private Worm next;
    private char c;

    Worm(int i,char x){
        System.out.println("Worm constructor: "+i);
        c = x;
        if(--i>0){
            next = new Worm(i,(char)(x+1));
        }
    }

    Worm(){
        System.out.println("Default constructor");
    }

    @Override
    public String toString() {
        String s = ": "+c+"(";
        for(int i = 0;i<d.length;i++) {
            s += d[i].toString();
        }
            s+=")";
            if(next!=null){
                s+=next.toString();
            }

        return s;
    }

    public static void main(String[] args){
        Worm worm = new Worm(6,'a');
        System.out.println("worm = "+worm);
        try {
            ObjectOutputStream outputStream =
                    new ObjectOutputStream(new FileOutputStream("worm.out"));
            outputStream.writeObject("Worm storage");
            outputStream.writeObject(worm);
            outputStream.close();

            ObjectInputStream inputStream =
                    new ObjectInputStream(new FileInputStream("worm.out"));
            String s = (String)inputStream.readObject();
            Worm worm1 = (Worm)inputStream.readObject();
            System.out.println(s+", w2 = "+worm1);

        }catch (FileNotFoundException e){

        }catch (IOException e){

        }catch (ClassNotFoundException e){

        }
    }

}

输出结果:

Worm constructor: 6
Worm constructor: 5
Worm constructor: 4
Worm constructor: 3
Worm constructor: 2
Worm constructor: 1
worm = : a(158): b(633): c(934): d(547): e(919): f(612)
Worm storage, w2 = : a(158): b(633): c(934): d(547): e(919): f(612)

我们可以看到,在对一个Serialiable对象进行重新装配的过程中,不会调用任何构造器。整个对象都是从InputStream中取得数据恢复的。

序列化控制
如果我们希望对象的某一部分序列化,或者某一个子对象完全不必序列化,此时,可是通过实现Externliable接口,用它替代Serialiable接口,便可控制序列化的具体过程。Externliable接口扩展了Serialiable,并增添了两个方法:writeExternal()readExternal()。在序列化装配的过程中,会自动调用这两个方法,以便我们执行一些特殊操作。

class Blip1 implements Externalizable {

    public Blip1() {
        System.out.println("Bilp1 Constructor");

    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Bilp1 writeExternal");
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Bilp1 readExternal");
    }
}


class Bilp2 implements Externalizable{


     Bilp2() {
        System.out.println("Bilp2 Constructor");

    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Bilp2 writeExternal");
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Bilp2 readExternal");
    }
}

public class Blips{

     public static void main(String[] args){
         Blip1 b1 = new Blip1();
         Bilp2 b2 = new Bilp2();

         try {
             ObjectOutputStream outputStream =
                     new ObjectOutputStream(new FileOutputStream("Blips.out"));
            System.out.println("Saving objects");
             outputStream.writeObject(b1);
             outputStream.writeObject(b2);
             outputStream.close();

             ObjectInputStream inputStream =
                     new ObjectInputStream(new FileInputStream("Blips.out"));

             System.out.println("Recovering b1");
             b1 = (Blip1)inputStream.readObject();

             System.out.println("Recovering b2");
             b2 = (Bilp2)inputStream.readObject();

         }catch (FileNotFoundException e){

         }catch (IOException e){

         }catch (ClassNotFoundException e){

         }
     }

}

结果:

Bilp1 Constructor
Bilp2 Constructor
Saving objects
Bilp1 writeExternal
Bilp2 writeExternal
Recovering b1
Bilp1 Constructor
Bilp1 readExternal
Recovering b2

上面结果,我们可以看到b2没有被恢复,这是因为在上面代码中,b2的构造器不是public的。我们可以看到,在恢复b1的时候,会调用Blip1的构造器,这是和Serialiable的不同。在Serialiable中,对象完全以它保存下来的二进制位为基础恢复,不存在构造器调用。而对Externalizable对象,所有普通的默认构建行为都会发生(包括在字段定义时的初始化)。

刚才我们有说,Externalizable可是让我们自己控制,我们看看下面一个例子:

class Blip3 implements Externalizable{

    public int x;
    public int y;
    public String s;

    public Blip3(String s) {
        super();
        System.out.println("Bilp3(String s)");
        this.s = s;
    }

    public Blip3() {

        System.out.println("Bilp3 Default Constructor");
    }

    @Override
    public String toString() {
        System.out.println("toString");
        return "toString:  "+s+"---"+x+"--"+y;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Blip3 writeExternal");
        out.writeObject(this.x);
        out.writeObject(this.s);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Blip3 readExternal");
        this.x = (int)in.readObject();
        this.s = (String) in.readObject();


    }
}


 public static void main(String[] args){
         Blip3 b3 = new Blip3("333333333");
         b3.x = 10;
         b3.y = 11;

         System.out.println(b3.toString());

         try {
             ObjectOutputStream outputStream =
                     new ObjectOutputStream(new FileOutputStream("Blip3.out"));
            System.out.println("Saving objects");
             //outputStream.writeObject(b1);
             //outputStream.writeObject(b2);
             outputStream.writeObject(b3);
             outputStream.close();

             ObjectInputStream inputStream =
                     new ObjectInputStream(new FileInputStream("Blip3.out"));

             System.out.println("Recovering b3");
             b3 = (Blip3) inputStream.readObject();
             System.out.println(b3.toString());

         }catch (FileNotFoundException e){
             e.printStackTrace();

         }catch (IOException e){
             e.printStackTrace();

         }catch (ClassNotFoundException e){
             e.printStackTrace();

         }
     }

输出结果:

Bilp3(String s)
toString
toString:  333333333---10--11
Saving objects
Blip3 writeExternal
Recovering b3
Bilp3 Default Constructor
Blip3 readExternal
toString
toString:  333333333---10--0

我们可以看到y是没有传过来的,而且Externalizable会调用的是默认构造器,如果没有默认构造器,会报错。

在使用Serializable序列化时,我们通常会指定一个serialVersionUID,这个serialVersionUID是来辅助序列化和反序列过程的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同,才能够被正常的反序列化。
serialVersionUID的工作机制:序列化的时候系统会把当前类的serialVersionUID写入系列化文件中(也可能是其他中介),当序列化的时候系统回去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一直就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化。否则就说明当前类和序列化的类相比发生了某些变化,比如成员变量的数量,类型可能发何时能了改变,这个时候是无法正常反序列化的。

Parcelable的使用

Parceable也是一个接口,只要实现这个接口,一个类的对象就可以实现序列化并通过Intent和Binder传递。

public class User implements Parcelable {
    
    public int userId;
    public String userName;
    public boolean isMale;


    public User(int userId, String userName, boolean isMale) {
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
    }

    private User(Parcel in){
        this.userId = in.readInt();
        this.userName = in.readString();
        this.isMale = in.readInt() == 1;
    }
    
    
    public static final Parcelable.Creator<User> CREATOR = new
            Parcelable.Creator<User>(){

                @Override
                public User createFromParcel(Parcel source) {
                    return new User(source);
                }

                @Override
                public User[] newArray(int size) {
                    return new User[size];
                }
            };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        
        dest.writeInt(userId);
        dest.writeString(userName);
        dest.writeInt(isMale?1:0);
    }
    
    public static void putUser(Context context,Class activity){
        Intent intent = new Intent(context,activity);
        Bundle bundle = new Bundle();
        bundle.putParcelable("user",new User(1,"vivian",false));
        intent.putExtra("user",bundle);
    }
}

Parcel内部包装了可序列化的数据,在意在Binder中自由传出。序列化使由writeToParcel方法来完成的,最终是通过Parcel中的一系列wirte方法来完成的。反序列功能右CREATOR来完成,其内部标明了如何创建序列化对象和数组,并通过一系列read方法来完成反序列化的过程。
内容描述功能又describeContents方法来完成,几乎所有情况下这个方法都应该返回0,仅当当前对象中存在文件描述符时,此方法返回1.

Serializable和Parcable的区别

可是肯定的是,两者都支持序列化和反序列的操作。
两者最大的区别在于存储媒介不同Serializable使用I/O读写存储在硬盘上,而Parcable是直接在内存中读写。很明显,内存的读写速度通常大于IO读写,所以在Android中传递数据优先选择Parcelable

Serializable会使用反射,序列化和反序列过程需要大量I/O操作,Parcelable自己实现封装和解封操作不需要用反射,数据也存放在Natvie内存中,效率要快很多,

在两个Activity之间传递对象需要注意:

对象的大小!!!
Intent 中的 Bundle 是使用 Binder 机制进行数据传送的。能使用的 Binder 的缓冲区是有大小限制的(有些手机是 2 M),而一个进程默认有 16 个 Binder 线程,所以一个线程能占用的缓冲区就更小了( 有人以前做过测试,大约一个线程可以占用 128 KB)。所以当你看到 The Binder transaction failed because it was too large 这类 TransactionTooLargeException 异常时,你应该知道怎么解决了。

参考:https://juejin.im/post/5a24fd8151882531ea651c37

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值