Android序列化

你真的会使用android序列化吗?

目录

1.Serializable接口。

User类

序列化

反序列化

完整代码:

布局

自动检测serialVersionUID

结果:

2.parcelable

自动生成Parcelable相关代码

3.Parcelable和Serializable的优缺点


 

1.Serializable接口。

Serializable接口的java提供的一个序列化接口。他可以实现序列化和反序列化。字需要在接口中实现就好,但是初次使用的时候很容易掉坑。就是没有标明serialVersionUID,private static final long serialVersionUID = 1L;。为什么要标明这个我们下面举例说明。

一般我们在使用的读写的时候都会先把对象序列化。先创建一个User类,并且实现一个Serializable接口。

User类

public class User implements Serializable {
    private int usetid;
    private String userName;
    private boolean isMale;
    private int age;

    public User() {
    }

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

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

    @Override
    public String toString() {
        return "User{" +
                "usetid=" + usetid +
                ", userName='" + userName + '\'' +
                ", isMale=" + isMale +
                ", age=" + age +
                '}';
    }
}接下来我们通过写入、读取本地文件来实现序列化反序列化。

序列化

User user = new User(2, s, true);
out = new ObjectOutputStream(new FileOutputStream(FEED_DIR));
out.writeObject(user);
out.close();

反序列化

ObjectInputStream in = new ObjectInputStream(new FileInputStream(FEED_DIR));
User user = (User) in.readObject();
in.close();

完整代码:

public class MainActivity extends AppCompatActivity {

    private Button button1;
    private Button button2;
    private TextView tv;
    private EditText et_num;
    private String FEED_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyCache/" + "cache.txt";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
    }


    private void initView() {
        button1 = findViewById(R.id.button1);
        button2 = findViewById(R.id.button2);
        tv = findViewById(R.id.tv);
        et_num = findViewById(R.id.et_num);

    }

    private void initData() {

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //todo:创建文件以及从中取值
                File feedTypeFile = new File(FEED_DIR);
                //检测sd卡是否安装
                if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                    //文件不存在
                    if (!feedTypeFile.exists()) {
                        try {
                            //先创建文件夹/目录
                            feedTypeFile.getParentFile().mkdirs();
                            //再创建新文件
                            feedTypeFile.createNewFile();
                        } catch (Exception e) {
                            e.printStackTrace();
                            tv.setText(e.toString());
                        }

                    }
                    String s = et_num.getText().toString();
                    User user = new User(2, s, true);
                    ObjectOutputStream out = null;
                    try {
                        out = new ObjectOutputStream(new FileOutputStream(FEED_DIR));
                        out.writeObject(user);
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                } else {
                    tv.setText("没有内存卡");
                }

            }
        });
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    ObjectInputStream in = new ObjectInputStream(new FileInputStream(FEED_DIR));
                    User user = (User) in.readObject();
                    in.close();
                    tv.setText(user.toString());
                } catch (Exception e) {
                    e.printStackTrace();
                    tv.setText(e.toString());
                }
            }
        });
    }
}

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="序列化" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="反序列化" />

    <EditText
        android:id="@+id/et_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="输入名称" />

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

这样做很简单就实现了序列化,但是因为没有声明serialVersionUID会有想不到的结果。我们继续

首先我们在user类里添加一个age属性

public class User implements Serializable {
    private int usetid;
    private String userName;
    private boolean isMale;
    private int age;

    public User() {
    }

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

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

    @Override
    public String toString() {
        return "User{" +
                "usetid=" + usetid +
                ", userName='" + userName + '\'' +
                ", isMale=" + isMale +
                ", age=" + age +
                '}';
    }
}

然后运行项目点击“反序列化”

报一个错

11-07 16:51:46.625 3664-3664/com.example.administrator.aidlsend W/System.err: java.io.InvalidClassException: com.example.administrator.aidlsend.User; Incompatible class (SUID): com.example.administrator.aidlsend.User: static final long serialVersionUID =6259768693016371241L; but expected com.example.administrator.aidlsend.User: static final long serialVersionUID =-9121888606695983862L;
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at java.io.ObjectInputStream.verifyAndInit(ObjectInputStream.java:2341)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at java.io.ObjectInputStream.readNewClassDesc(ObjectInputStream.java:1643)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:657)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1782)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:761)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1983)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1940)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at com.example.administrator.aidlsend.MainActivity$2.onClick(MainActivity.java:87)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at android.view.View.performClick(View.java:4780)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at android.view.View$PerformClick.run(View.java:19866)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at android.os.Handler.handleCallback(Handler.java:739)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at android.os.Looper.loop(Looper.java:135)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5254)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at java.lang.reflect.Method.invoke(Method.java:372)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
        11-07 16:51:46.630 3664-3664/com.example.administrator.aidlsend W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)
        11-07 16:51:49.668 1457-1629/? D/ld_audio_hw: lineFun, out_standby

意思就是说序列化和反序列化的serialVersionUID不一致,发反序列化失败。

原因就是当我们在没有设置的时候,系统会计算一个对应的hash值付给serialVersionUID,当我们改变属性的时候,系统会再次计算并把hash值付给serialVersionUID,所以两次的UID不一致自然无法反序列化。所以我们需要手动声明一个serialVersionUID,这个值可以是自己定义的比如:1L。也可以是系统计算的hash值。

  那么每次使用Seriailzable的时候忘记添加,或者添加重复怎么办,如何获取系统计算的hash值呢?

自动检测serialVersionUID

之后就会有提示了

然后Alt+Enter 提示添加

结果:

private static final long serialVersionUID = 2602503210684455922L;

2.parcelable

        parcelable是android为我们提供的一个实现序列化的接口,实现这个类就给可以通过Inten和Binder传递数据。但是相对于Serializable来说,在代码量上来说他更加的复杂一些。

public class Persion implements Parcelable{
    private String name;
    private String id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Persion(String name, String id) {
        this.name = name;
        this.id = id;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.name);
        dest.writeString(this.id);
    }

    public Persion() {
    }

    protected Persion(Parcel in) {
        this.name = in.readString();
        this.id = in.readString();
    }

    public static final Creator<Persion> CREATOR = new Creator<Persion>() {
        @Override
        public Persion createFromParcel(Parcel source) {
            return new Persion(source);
        }

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

增加了这么几个方法

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

describeContents() 功能:返回当前对象的内容描述,如果含有文件描述符返回1,否则返回0,几乎所有情况返回0.

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(this.name);
    dest.writeString(this.id);
}

writeToParcel(Parcel dest,int flags) 功能:将当前对象写入序列化结构中,其中flags标识有两种值:0或1.为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0

protected Persion(Parcel in) {
    this.name = in.readString();
    this.id = in.readString();
}

persion(Parcel in)工能:从序列化后的对象中创建原始的对象。

public static final Creator<Persion> CREATOR = new Creator<Persion>() {
    @Override
    public Persion createFromParcel(Parcel source) {
        return new Persion(source);
    }

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

createFromParcel(Parcel source) 功能:从序列化后的对象中创建原始对象。

newArray(int size) 功能:创建指定长度的原始对象数组。

 

以上可以看到各种Parcel。那个这个Parcel是做什么的呢?

        里面的这个Parcel,是内部包装了可序列化的数据,可以在Binder中只有的传输。在序列化过程中需要实现的功能有序列化,反序列化和内容描述。其中序列化功能由writeToParcel方法完成,最终是通过Parcel中的一系列write方法来完成的。反序列化由CREATOR来完成,其内部标明了如何创建序列化和数组,并通过Parcel中一些列read方法来完成反序列化的过程。内容描述功能由describeContents方法来完成。几乎在所有情况下这个方法的返回值都为0,仅在当前对象中存在文件描述符时,此方法返回1.

        那么每次都要手写这么多代码吗?当然不用,下面介绍如何快速实现这些代码。

自动生成Parcelable相关代码

 

首先 Settings ->Plugins->Borwse repositories->搜索 Parcelabe->安装->重启

 使用:Alt+Insert

点击OK 就可以了

3.Parcelable和Serializable的优缺点

Serializable是java中的序列化接口。使用简单但是开销大,序列化与反序列化需要大量的I/O操作。

Parcelable是Android中的序列化接口。更加适合在android平台使用,使用起来复杂一下,但是效率高。

使用:Parcelable主要用在内存上的序列化

           Serializable适合将对象储存在设备中,和将对象序列化后通过网络传输的情况下使用

 

转发标明出处

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大不懂

码字不易,一块也是爱,么么

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值