Android - 序列化 --- Serializable、Parcelable

序列化:在数据传输中,如果要传输的是对象,这时候就用到我们的序列化和反序列化。有时候想将对象持久化储存在设备或者网络上,这时建议使用 Serializable
  • Serializable : java 提供的接口 ,使用简单,直接实现这个接口就行。下面是一个使用栗子。
public class User implements Serializable {
   private static final long serialVersionUID = 1L; // 防止java.io.InvalidClassException

    //private String testError;

    private int userId;

    private String userName;

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

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

值得注意的是: private static final long serialVersionUID = 1L;
建议我们使用 Serializable 接口的时候都手动去指定,虽然是非必须的,但是:如果不指定 serialVersionUID ,User 类序列化后,如果中途增加或减少一个成员变量(比如新增 private String testError; ),这时我们去反序列化将报错:java.io.InvalidClassException

为什么?序列化的时候系统把当前类的serialVersionUID 写入系列化文件中,当反序列化的时候会去检测文件中的serialVersionUID ,如果一致说明两者版本相同,否则说明两者相比发生了某些变化 ,这时就报错。

 /**
     * 序列化保存 Serializable 数据
     *
     * @param user
     */
    private void saveAsSerializable(User user) {
        Log.d("saveAsSerializable", "User:" + user.getUserName() + " " + user.getUserId());
        FileOutputStream fos;
        ObjectOutputStream oos;
        try {
            fos = getApplicationContext().openFileOutput("cache1.txt",
                    Context.MODE_PRIVATE);
            oos = new ObjectOutputStream(fos);
            oos.writeObject(user);
            oos.close();
            fos.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 反序列化读取 Serializable 数据
     *
     * @return
     */
    private User readSerializable() {
        User obj = null;
        FileInputStream fis;
        ObjectInputStream ois;
        try {
            fis = getApplicationContext().openFileInput("cache1.txt");
            ois = new ObjectInputStream(fis);
            obj = (User) ois.readObject();
            ois.close();
            fis.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        if (obj != null) {
            Log.d("readSerializable", "User:" + obj.getUserName() + " " + obj.getUserId());
            return obj;
        } else {
            return null;
        }

    }

  • Parcelable:
    Android 专用的一个接口,经别人测试,性能比 Serializable 好10倍左右,当在对象持久化储存时不建议使用这个。

下面是个栗子,其实编译器已经帮忙生成了大部分代码,我们只要写getset 和构造函数就行:

public class Human implements Parcelable {
    private String address;
    private boolean isMan;
    private Woman woman;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public boolean isMan() {
        return isMan;
    }

    public void setMan(boolean man) {
        isMan = man;
    }

    public Woman getWoman() {
        return woman;
    }

    public void setWoman(Woman woman) {
        this.woman = woman;
    }

    public Human(String address, boolean isMan, Woman woman) {
        this.address = address;
        this.isMan = isMan;
        this.woman = woman;
    }

    protected Human(Parcel in) {
        address = in.readString();
        isMan = in.readInt() == 1;
        //反序列化 Woman 属性,由于 Woman 是另一个可序列化对象,所以它的反序列化过程需要传递当前线程的上下文类加载器,否则会报无法找到该类的错误。
        woman = in.readParcelable(Woman.class.getClassLoader());
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public Human createFromParcel(Parcel in) {
            return new Human(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(address);
        parcel.writeInt(isMan ? 1 : 0);
        parcel.writeParcelable(woman, i);
    }
}

这里敲一下黑板:

  1. 需要注意的是 Parcelable 不支持布尔值传输,怎样使用请看上面栗子;

  2. 当我们序列化的类中有其他对象,如例子上的 Woman 类 ,那么这个类也必须实现 Parcelable 接口,同时:反序列化 Woman 属性,由于 Woman 是另一个可序列化对象,所以它的反序列化过程需要传递当前线程的上下文类加载器,否则会报无法找到该类的错误(实现Parcelable 接口生成代码的时候已经自动写了)。(这里得说一下:生成代码前先把所有的成员变量都写了,方便自动生成,不然中途调加变量要相应的补上序列化和反序列化的实现)

下面是 Parcelable 的序列化与反序列化持久化存储的代码,建议 Intent 传输用 Parcelable 。

 /**
     * 序列化保存 Parcalable 数据
     *
     * @return
     */
    private void saveParce(Human human) {
        Log.d("loadParce", "Human:" + human.getAddress() + " " + human.getWoman().getName());
        FileOutputStream fos;
        try {
            fos = getApplicationContext().openFileOutput("cache2.txt",
                    Context.MODE_PRIVATE);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            Parcel parcel = Parcel.obtain();
            parcel.writeParcelable(human, 0);

            bos.write(parcel.marshall());
            bos.flush();
            bos.close();
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 反序列化读取 Parcalable 数据
     *
     * @return
     */
    private void loadParce() {
        FileInputStream fis;
        try {
            fis = getApplicationContext().openFileInput("cache2.txt");
            byte[] bytes = new byte[fis.available()];
            fis.read(bytes);
            Parcel parcel = Parcel.obtain();
            parcel.unmarshall(bytes, 0, bytes.length);
            parcel.setDataPosition(0);

            Human data = parcel.readParcelable(Human.class.getClassLoader());
            Log.d("loadParce", "Human:" + data.getAddress() + " " + data.getWoman().getName()+"isMan: "+ data.isMan());
            fis.close();

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

用 Intent 传输就贼简单了, putExtra 传过去后,采用Serializable 接口的用 getIntent().getSerializableExtra ()、采用 Parcelable 接口的用getIntent().getParcelableExtra() 取出来就行。下面是网上看到的别人使用的代码(采用 Parcelable 接口):

MainActivity 中

                Intent mTvOpenThird = new 
                Intent(MainActivity.this,ThirdActivity.class);
                Pen tranPen = new Pen();
                tranPen.setColor("big red");
                tranPen.setSize(98);
                // public Intent putExtra(String name, Parcelable value)
                mTvOpenThird.putExtra("parcel_test",tranPen);
                startActivity(mTvOpenThird);


ThirdActivity 中

        Pen pen = (Pen)getIntent().getParcelableExtra("parcel_test");
        mTvThirdDate = (TextView) findViewById(R.id.mTvThirdDate);
        mTvThirdDate.setText("颜色:"+pen.getColor()+"\\n"
                            +"大小:"+pen.getSize());
  • 小结一下:Serializable 使用简单但开销较大,序列化和反序列化过程需要大量I/O操作,建议都指定 serialVersionUID ,随便什么数值。所以如果考虑效率的话首选 Parcelable,Parcelable 主要用在内存序列化上,如果将对象持久化保存建议使用 Serializable 。Demo

你可能感兴趣的:(Android - 序列化 --- Serializable、Parcelable)