美文网首页
Android序列化学习

Android序列化学习

作者: 留给时光吧 | 来源:发表于2018-04-08 22:43 被阅读0次

    在Android中有两种接口可以实现序列化操作:Serializable和Parcelable。前者是Java API中带的,后者则是Android API中的。我们一次来学习一下。

    Serializable接口

    利用这个接口是实现序列化很简单,只需在相应的类中实现这个接口即可,而且不用实现任何方法,如下

    public class Item implements Serializable{
        public int id;
        public String msg;
    
        Item(int id,String msg){
            this.id = id;
            this.msg = msg;
        }
    }
    

    用法很简单,但有一点需要注意,我们在看这个接口介绍时需要有一个serialVersionUID,或者我们看源码时,有些实现了该接口的类都有这个ID,如ArrayList:

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
        private static final long serialVersionUID = 8683452581122892189L;
        ...
    }
    

    但用过的朋友可能会发现,不写这个UID也能正常实现序列化和反序列话,那么这个UID有什么用呢?这里有一点需要注意的是,如果我们不写这个UID,是不是真的就没有呢,事实上,如果不写编译器会自动根据类的内容计算一个hash值,作为这个类的UID。那么这个值又有什么用呢?既然是ID肯定是作为一个标识符,只要标识符一样,我们在反序列的时候即使类有改变,也能尽可能的还原数据,示例如下:

    我们先手动指定一个UID

    public class Item implements Serializable{
        public static final long serialVersionUID = 1L;
    
        public int id;
        public String msg;
    
        Item(int id,String msg){
            this.id = id;
            this.msg = msg;
        }
    }
    

    在执行序列化操作:

           try {
                File file = new File(getFilesDir(),"Serializable");
                if (file.exists())
                    file.delete();
                ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
                out.writeObject(new Item(10,"a"));
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    

    此时我们如果不做更改,直接反序列化,肯定是可以的。但是我们现在对Item类添加一个成员变量,但不改变其UID:

    public class Item implements Serializable{
        public static final long serialVersionUID = 1L;
    
        public int id;
        public String msg;
        public String msg2;
    
        Item(int id,String msg,String msg2){
            this.id = id;
            this.msg = msg;
            this.msg2 = msg2;
        }
    }
    

    此时在再执行反序列化操作,如下

           try {
                ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File(getFilesDir(),"Serializable")));
                Item item = (Item) in.readObject();
                in.close();
                Toast.makeText(this,item.id+item.msg,Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                Toast.makeText(this,e.getMessage(),Toast.LENGTH_SHORT).show();
            }
    

    发现是可以正确解析到数据的,此时如果我们删掉那个手动指定的UID会如何呢?结果会出现下面异常:



    大致意思就是序列化文件里的UID为1,而本地类的UID变成了这么一长串数字,可见我们即使不写这个ID,编译器也会给我们加上,一旦类发生改变,ID就会改变,导致反序列化失败,若是UID相同,即使类有改变,也会尽可能的恢复数据。但是并不是所有情况都可以,比如类名的改变,自然是不可能恢复的

    Parcelable接口

    相比Serializable,这个接口就复杂的多了,一个简单的例子如下:

    public class Item implements Parcelable{
    
        public int id;
        public String msg;
    
        Item(int id,String msg){
            this.id = id;
            this.msg = msg;
        }
    
        protected Item(Parcel in) {
            id = in.readInt();
            msg = in.readString();
        }
    
        public static final Creator<Item> CREATOR = new Creator<Item>() {
            @Override
            public Item createFromParcel(Parcel in) {
                return new Item(in);
            }
    
            @Override
            public Item[] newArray(int size) {
                return new Item[size];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(id);
            dest.writeString(msg);
        }
    }
    

    对比得知,Serializable的序列化方式并不指明具体的实现方式,而Parcelable则根据具体的方法来实现,比较清晰。如序列化过程由writeToParcel完成,其实就是将各个数据写到一个Parcel对象里,反序列化由一个静态的Creator实例化对象完成,调用其中的createFromParcel方法即可。虽然要实现的方法比较复杂,但是如果你使用的是Android Studio,其中的代码智能补全可以帮你把所有事情都完成。下面我问就写一个例子,应用一下。

    Intent intent = new Intent();
    intent.putExtra("item",new Item(10,"s"));
    intent.setClass(getApplicationContext(),SecondActivity.class);
    startActivity(intent);
    
    Item item = getIntent().getParcelableExtra("item");
    Toast.makeText(this,item.id+item.msg,Toast.LENGTH_SHORT).show();
    

    还有一点我们要清楚,Parcelable是Android里面的,所以不能用于IO序列化对象,但能用在Intent传递数据或其他方面。另外Parcelable中还有一个describeContents方法,这个方法在含有文件描述符时应该返回1,其余几乎所有情况都返回0.

    两种序列化的比较

    一般而言,Serializable时借助IO的,需要大量IO操作,比较消耗性能,Parcelable都是在内存中完成的,效率比较高。但是Parcelable不能进行数据持久化,这时还需要Serializable

    相关文章

      网友评论

          本文标题:Android序列化学习

          本文链接:https://www.haomeiwen.com/subject/jpamhftx.html