Android 序列化 - Serializable、Parcelable

序列化:在數(shù)據(jù)傳輸中,如果要傳輸?shù)氖菍?duì)象,這時(shí)候就用到我們的序列化和反序列化。有時(shí)候想將對(duì)象持久化儲(chǔ)存在設(shè)備或者網(wǎng)絡(luò)上,這時(shí)建議使用 Serializable
  • Serializable : java 提供的接口 ,使用簡(jiǎn)單,直接實(shí)現(xiàn)這個(gè)接口就行。下面是一個(gè)使用栗子。
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 接口的時(shí)候都手動(dòng)去指定,雖然是非必須的,但是:如果不指定 serialVersionUID ,User 類(lèi)序列化后,如果中途增加或減少一個(gè)成員變量(比如新增 private String testError; ),這時(shí)我們?nèi)シ葱蛄谢瘜?bào)錯(cuò):java.io.InvalidClassException

為什么?序列化的時(shí)候系統(tǒng)把當(dāng)前類(lèi)的serialVersionUID 寫(xiě)入系列化文件中,當(dāng)反序列化的時(shí)候會(huì)去檢測(cè)文件中的serialVersionUID ,如果一致說(shuō)明兩者版本相同,否則說(shuō)明兩者相比發(fā)生了某些變化 ,這時(shí)就報(bào)錯(cuò)。

 /**
     * 序列化保存 Serializable 數(shù)據(jù)
     *
     * @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 數(shù)據(jù)
     *
     * @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 專(zhuān)用的一個(gè)接口,經(jīng)別人測(cè)試,性能比 Serializable 好10倍左右,當(dāng)在對(duì)象持久化儲(chǔ)存時(shí)不建議使用這個(gè)。

下面是個(gè)栗子,其實(shí)編譯器已經(jīng)幫忙生成了大部分代碼,我們只要寫(xiě)getset 和構(gòu)造函數(shù)就行:

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 是另一個(gè)可序列化對(duì)象,所以它的反序列化過(guò)程需要傳遞當(dāng)前線程的上下文類(lèi)加載器,否則會(huì)報(bào)無(wú)法找到該類(lèi)的錯(cuò)誤。
        woman = in.readParcelable(Woman.class.getClassLoader());
    }

    public static final Creator<Human> CREATOR = new Creator<Human>() {
        @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 不支持布爾值傳輸,怎樣使用請(qǐng)看上面栗子;

  2. 當(dāng)我們序列化的類(lèi)中有其他對(duì)象,如例子上的 Woman 類(lèi) ,那么這個(gè)類(lèi)也必須實(shí)現(xiàn) Parcelable 接口,同時(shí):反序列化 Woman 屬性,由于 Woman 是另一個(gè)可序列化對(duì)象,所以它的反序列化過(guò)程需要傳遞當(dāng)前線程的上下文類(lèi)加載器,否則會(huì)報(bào)無(wú)法找到該類(lèi)的錯(cuò)誤(實(shí)現(xiàn)Parcelable 接口生成代碼的時(shí)候已經(jīng)自動(dòng)寫(xiě)了)。(這里得說(shuō)一下:生成代碼前先把所有的成員變量都寫(xiě)了,方便自動(dòng)生成,不然中途調(diào)加變量要相應(yīng)的補(bǔ)上序列化和反序列化的實(shí)現(xiàn))

下面是 Parcelable 的序列化與反序列化持久化存儲(chǔ)的代碼,建議 Intent 傳輸用 Parcelable 。

 /**
     * 序列化保存 Parcalable 數(shù)據(jù)
     *
     * @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 數(shù)據(jù)
     *
     * @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 傳輸就賊簡(jiǎn)單了, putExtra 傳過(guò)去后,采用Serializable 接口的用 getIntent().getSerializableExtra ()、采用 Parcelable 接口的用getIntent().getParcelableExtra() 取出來(lái)就行。下面是網(wǎng)上看到的別人使用的代碼(采用 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());
  • 小結(jié)一下:Serializable 使用簡(jiǎn)單但開(kāi)銷(xiāo)較大,序列化和反序列化過(guò)程需要大量I/O操作,建議都指定 serialVersionUID ,隨便什么數(shù)值。所以如果考慮效率的話首選 Parcelable,Parcelable 主要用在內(nèi)存序列化上,如果將對(duì)象持久化保存建議使用 Serializable 。Demo
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容