Android序列化学习
在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 extends AbstractList implements List, 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- CREATOR = new Creator
- () { @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(安卓)AOP之AspectJ入门
- Android(安卓)DataBinding使用详解(一)
- android 【点击输入框调出输入法前的】输入框获取焦点和输入法的
- 使用 SQLiteDatabase 操作 SQLite 数据库
- 转:ANDROID音频系统散记之四:4.0音频系统HAL初探
- Android(安卓)studio 中与本地 html 页面交互
- Android(安卓)开发者必知必会的权限管理知识
- 真机上使用Hierarchy Viewer
- Android(安卓)studio 命令gradlew assembleRelease打包时,出现 Un