欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 序列化的存储和读取总结及简单使用

程序员文章站 2024-02-29 08:38:22
       android 序列化 1.序列化的目的   (1).永久的保存对象数据(...

       android 序列化

1.序列化的目的

  (1).永久的保存对象数据(将对象数据保存在文件当中,或者是磁盘中

  (2).通过序列化操作将对象数据在网络上进行传输(由于网络传输是以字节流的方式对数据进行传输的.因此序列化的目的是将对象数据转换成字节流的形式)

  (3).将对象数据在进程之间进行传递(activity之间传递对象数据时,需要在当前的activity中对对象数据进行序列化操作.在另一个activity中需要进行反序列化操作讲数据取出)

  (4).java平台允许我们在内存中创建可复用的java对象,但一般情况下,只有当jvm处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比jvm的生命周期更长(即每个对象都在jvm中)但在现实应用中,就可能要停止jvm运行,但有要保存某些指定的对象,并在将来重新读取被保存的对象。这是java对象序列化就能够实现该功能。(可选择入数据库、或文件的形式保存)

  (5).序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化.

  (6).在intent之间,基本的数据类型直接进行相关传递即可,但是一旦数据类型比较复杂的时候,就需要进行序列化操作了.

       android中序列化的实现有两种方式:serializable接口和parcelable接口,本文对这两种方式进行简单的总结和使用。

一.相关概念

(一)序列化的原因(序列化能实现的效果)

1.永久性保存对象,保存对象的字节序列到本地文件中;

2.对象在网络中传递;3.对象在ipc间传递。

(二)序列化的方法

       在android系统中关于序列化的方法一般有两种,分别是实现serializable接口和parcelable接口,其中serializable接口是来自java中的序列化接口,而parcelable是android自带的序列化 接口。 上述的两种序列化接口都有各自不同的优缺点,我们在实际使用时需根据不同情况而定。

1.当需要内存较多时使用parcelable接口。

       serializable在序列化的时候会产生大量的临时变量,从而引起频繁的gc,而相比之下 parcelable的性能更高(毕竟是android自带的),所以当在使用内存时(如:序列化对象在网络中传递对象或序列化在进程间传递对象),更推荐使用parcelable接口。

2.当需要本地存储时,使用serializable 接口。

       但parcelable有个明显的缺点:不能能使用在要将数据存储在磁盘上的情况(如:永久性保 存对象,保存对象的字节序列到本地文件中),因为parcel本质上为了更好的实现对象在 ipc间传递,并不是一个通用的序列化机制,当改变任何parcel中数据的底层实现都可能导致之前的数据不可读取,所以此时还是建议使用serializable 。

二.serializable接口的使用

       serializable的接口实现很简单,只需让需要序列化的类继承serializable即可,系统会自动将其序列化。存储时使用fileoutputstream构造一个objectoutputstream,使用writeobject 存储对象。读取时使用fileinputstream构造一个objectinputstream,使用readobject读取对象。

(一)布局文件activity_main.xml的设计

<linearlayout
   xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
   >

  <edittext 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/main_et_name"
    android:hint="你的用户名"
    />
    <edittext 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/main_et_password"
    android:hint="你的密码"
    />
     <edittext 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/main_et_age"
    android:hint="你的年龄"
    />

  <button
    android:onclick="save"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="保存数据" />

   <button
    android:onclick="read"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="读取数据" />

   <textview 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="数据"
    android:id="@+id/main_tv"
    />
</linearlayout>

       界面设计:通过几个输入框输入数据,两个按钮一个保存数据一个读取数据,读取的数据显示在一个文本框下。

(二)创建一个属性类继承serializable

package com.example.lesson18_serializable;
import java.io.serializable;
/**
 *属性类,用来存储数据,继承接口serializable,但是什么方法都不用重写!
 */
public class people implements serializable{
  //定义基本信息
  string name;
  string password;
  int age;
  //无参构造方法
  public people() {
    super();
  }
  //有参构造方法,方便数据写入
  public people(string name, string password, int age) {
    super();
    this.name = name;
    this.password = password;
    this.age = age;
  }

  //重写tostring方法,方便显示
  @override
  public string tostring() {
    return "people [name=" + name + ", password=" + password + ", age="
        + age ;
  }

}

(三)主方法的类

package com.example.lesson18_serializable;

import java.io.file;
import java.io.fileinputstream;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.io.objectinputstream;
import java.io.objectoutputstream;

import android.app.activity;
import android.os.bundle;
import android.os.environment;
import android.util.log;
import android.view.view;
import android.widget.edittext;
import android.widget.textview;

public class mainactivity extends activity {

  //保存文件的路径
  string path=environment.getexternalstoragedirectory().getabsolutepath()+"/people.txt";
  //定义布局内的控件
  edittext edit_name;
  edittext edit_password;
  edittext edit_age;
  textview text;


  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_main);
    //实例化布局控件
    edit_name=(edittext) findviewbyid(r.id.main_et_name);
    edit_password=(edittext) findviewbyid(r.id.main_et_password);
    edit_age=(edittext) findviewbyid(r.id.main_et_age);
    text=(textview) findviewbyid(r.id.main_tv);
  }

  //保存数据
  public void save(view view){  
      objectoutputstream fos=null;
    try {

      //如果文件不存在就创建文件
      file file=new file(path);
      //file.createnewfile();
      //获取输出流
      //这里如果文件不存在会创建文件,这是写文件和读文件不同的地方
      fos=new objectoutputstream(new fileoutputstream(file));
      //获取输入框内的文件进行写入
      string name=edit_name.gettext().tostring();
      string password=edit_password.gettext().tostring();
      int age=integer.parseint(edit_age.gettext().tostring());
      people people=new people(name, password, age);
      //这里不能再用普通的write的方法了
      //要使用writeobject
      fos.writeobject(people);;
    } catch (exception e) {
      e.printstacktrace();
    }finally{
      try {
        if (fos!=null) {
          fos.close();
        }
      } catch (ioexception e) {
      }

    }

  }

  //读取数据
  public void read(view view){
    objectinputstream ois=null;
    try {
      log.e("tag", new file(path).getabsolutepath()+"<---");
      //获取输入流
      ois=new objectinputstream(new fileinputstream(new file(path)));
      //获取文件中的数据
      object people=ois.readobject();
      //把数据显示在textview中
       text.settext(people.tostring());
    } catch (exception e) {
      e.printstacktrace();
    }finally{
      try {
        if (ois!=null) {
          ois.close();
        }
      } catch (ioexception e) {
        e.printstacktrace();
      }
    }
  }

}

这里使用但是外部存储的方式来存储数据,需要添加权限:

<uses-permission android:name="android.permission.write_external_storage"/>

程序运行后的界面:
Android 序列化的存储和读取总结及简单使用

输入对应的信息,点击保存,再点击读取显示的结果:
Android 序列化的存储和读取总结及简单使用

其中这里的数据是保存再本地文件中的,下次不用写入数据,可以直接读取上次写入的文件。

三.parcelable接口的使用

       使用的方法过程要麻烦一些!

实现parcelable接口主要可以分为一下几步:

1.让属性类model实现parcelable接口2.重写writetoparcel方法,将你的对象序列化为一个parcel对象,

即:将类的数据写入外部提供的parcel中,打包需要传递的数据到parcel容器保存,以便从parcel容器获取数据。 这里的文件的写入方法非常重要。

3.重写describecontents方法,内容接口描述,默认返回0即可。 这个方法基本没有用!4.实例化静态内部对象creator实现接口parcelable.creator,并重写读取的抽象方法。

       这里的读取的方法也是很重要的,必须和写的时候的顺序是一致的。这里的creator接口对象的名字是固定的,如果改成其他名字底层会识别不到这个接口!

       注意:若将parcel看成是一个流,则先通过writetoparcel把对象写到流里面,再通过 createfromparcel从流里读取对象,因此类实现的写入顺序和读出顺序必须一致。

       这里设计程序从一个页面跳转到另一个页面,并把对象的数据传递过去。

(一)设计属性类继承parcelable接口

package com.example.lesson18_parcalable;
import android.os.parcel;
import android.os.parcelable;
/**
 *属性类,继承parcelable
 *实现两个方法,在其中一个方法内实现对象写入的操作
 *创建一个接口类creator,重写读取对象的方法
 */
public class user implements parcelable{

  //user的各种数据的定义
  string name;
  string password;
  int age;
  double money;
  boolean isadmin;

  public user(){}

  //写一个构造方法来方便写入数据
  public user(string name, string password, int age, double money,
      boolean isadmin) {
    super();
    this.name = name;
    this.password = password;
    this.age = age;
    this.money = money;
    this.isadmin = isadmin;
  }

  @override
  // 这个方法没什么用
  public int describecontents() {
    return 0;
  }

  @override
  // 写数据的底层实现 
  public void writetoparcel(parcel arg0, int arg1) {
     arg0.writestring(name);
     arg0.writestring(password);
     arg0.writeint(age);
     arg0.writedouble(money);
     //把布尔类型的数据做处理,true1,false0
     arg0.writeint(isadmin?1:0);
  }

  //实例化静态内部对象creator实现接口,creator名字不能改变,否则会报错
  public static creator creator=new creator<user>() {
    @override
    // 读书数据的底层实现,要和写入的数据的顺序保持一致
    public user createfromparcel(parcel arg0) {
      user user=new user();
      user.name=arg0.readstring();
      user.password=arg0.readstring();
      user.age=arg0.readint();
      user.money=arg0.readdouble();
      //布尔类型的数据要处理
      user.isadmin=arg0.readint()==1?true:false;
      return user;
    }

    @override
    public user[] newarray(int arg0) {
       //返回
      return new user[arg0];
    }
  };

  //从tostring方法
  @override
  public string tostring() {
    return "user [name=" + name + ", password=" + password + ", age=" + age
        + ", money=" + money + ", isadmin=" + isadmin + "]";
  }

}

(二)主方法的类的设计

package com.example.lesson18_parcalable;

import android.app.activity;
import android.content.intent;
import android.os.bundle;
import android.view.view;
import android.view.view.onclicklistener;
import android.widget.button;

public class mainactivity extends activity {

  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate); 

    button button=new button(this);
    button.settext("跳转到b页面");

    setcontentview(button);
    button.setonclicklistener(new onclicklistener() {      
      @override
      public void onclick(view arg0) {
         //跳转到另一个页面,对象的数据也要传递过去
        intent intent=new intent(mainactivity.this,otheractivity.class);
        //定义数据
        user user=new user("liwenzhi","123456",22,1000000,true);
        //把数据放到intent对象里面
        intent.putextra("user", user);
        //实现页面跳转
        startactivity(intent);
      }
    });

  }
}

        上面这个类也是很简单的。设计一个按钮监听跳转到另一个页面。

(三)另一个页面的设计

package com.example.lesson18_parcalable;
import android.app.activity;
import android.os.bundle;
import android.widget.textview;

public class otheractivity extends activity{
  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    textview textview=new textview(this);
    textview.settextsize(30);
    //获取传递过来的数据
    user user=getintent().getparcelableextra("user");
    textview.settext(user.tostring());   
    setcontentview(textview);



  }

}

       上面的页面也是比较简单的,接收从上一个页面传递过来的对象,然后显示在一个textview。

程序运行后的显示界面:
Android 序列化的存储和读取总结及简单使用

点击大按钮后,显示的界面:

Android 序列化的存储和读取总结及简单使用

上面的数据的写死的,其实也是可以向第一个程序那样使用几个输入框来确定数据的。

       对比这两个接口实现的方法和效果: 

       对于第一个程序使用serializable实现了数据的传递,并且数据是保存在本地的,即使是程序被卸载了,其他程序只要是文件路径正确,也可以访问保存的文件的数据,也是可以用来做进程间的通信的,但是这样需要消耗一些内存。  

     对比第二个程序使用parcalable实现了数据的传递,这里的数据是不能保存到本地的,占用的内存较少,比较适合用于进程间的数据传递。
对于应用方面:网络信息传递和进程间数据传递使用parcalable实现了数据的传递的方式是比较多一点的。 

       对于这两种数据传递的信息大小一般不能是很大的数据。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!