该文章是一个系列文章,是本人在 Android 开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了《Android 开发艺术探索》以及《深入理解 Android 卷 Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其他的优质博客,在此向各位大神表示感谢,膜拜!!!另外,本系列文章知识可能需要有一定 Android 开发基础和项目经验的同学才能更好理解,也就是说该系列文章面向的是 Android 中高级开发工程师.
前言
上一篇中我们比较详尽的分析了 ServiceManager.那么本篇我们来讲一下 Android 序列化的相关知识.为什么跨度那么大,因为 "任性"?其实不是的,同志们还记得上两篇出现的 Parcel 吗,Parcel 是一个容器,他可以包含数据或者是对象引用,并且能够用于 Binder 的传输.同时支持序列化以及跨进程之后进行反序列化,同时其提供了很多方法帮助开发者完成这些功能.从上面的描述可以看出 Parcel 是进程间通信的数据载体.我们常常需要持久化一些对象,除了数据库等持久化方案之外,把对象转换成字节数组并通过流的方式存储在本地也是一个不错的方法,另外当我们需要通过 Intent 和 Binder 传输数据是就需要使用序列化后的数据.
Java 中的 Serializable
Serializable 是 Java 所提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作.使用 Serializable 来实现序列化相当简单,只需要在需要序列化的类实现 Serializable 接口并在其中声明一个类似下面的标识即可自动实现默认的序列化过程.
* 与writeReplace相同,ObjectInputStream会通过反射调用 readResolve()这个方法,
public class Person extends PersonParent implements Serializable {
private static final long serialVersionUID = 1L;
//静态域
public static int static_field;
//transient域
public transient int transient_field;
//一个普通的域
public String desc;
public Person(String desc) {
this.desc = desc;
}
static class PersonSerializableProxy implements Serializable {
private String desc;
private PersonSerializableProxy(Person s) {
this.desc = s.desc;
}
/***/
* 决定是否替换反序列化出来的对象.
* 在序列化一个对象时,ObjectOutputStream会通过反射首先调用writeReplace这个方法,
* @return
*/
private Object readResolve() {
return new Person(desc);
}
}
/**
*
* 在这里我们可以替换真正送去序列的对象,
* 如果我们没有重写,那序列化的对象就是最开始的对象.
* 这里主要是为了防止攻击,任何以Person声明的对象字节流都是流氓!!
* @return
*/
private Object writeReplace() {
//序列化Person的时候我们并没有直接写入Person对象,而是写入了PersonSerializableProxy对象
return new PersonSerializableProxy(this);
}
/**
* 因为我在writeReplace中已经把序列化的实例指向了SerializableProxy
不过在使用中也需要注意以下几个问题:
* @param stream * @throws InvalidObjectException * /
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("proxy requied!");
}
public static void main(String[] args) throws IOException,
ClassNotFoundException {
Person person = new Person("desc");
person.transient_field = 100;
person.static_field = 10086;
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cache.txt"));
outputStream.writeObject(person);
outputStream.flush();
outputStream.close();
person.static_field = 10087;
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("cache.txt"));
Person deserialObj = (Person) objectInputStream.readObject();
System.out.println(deserialObj);
}
}
class PersonParent {
private String name;
/ / PersonParent类要么继承自Serializable,
要么需要提供一个无参构造器.public PersonParent() {}
public PersonParent(String name) {
this.name = name;
}
}
serialVersionUID 用来标识当前序列化对象的类版本,建议每一个实现 Serialization 的类都指定该域.当然如果我们没有指定,JVM 会根据类的信息自动生成一个 UID.
被 transient 描述的域和类的静态变量是不会被序列化的,序列化是针对类实例.
需要进行序列化的对象所有的域都必须实现 Serializable 接口,不然会直接报错 NotSerializableException.当然,有两个例外:域为空 或者域被 transient 描述是不会报错的.
如果一个实现了 Serializable 类的对象继承自另外一个类,那么这个类要么需要继承自 Serializable,要么需要提供一个无参构造器.
反序列化产生的对象并不是通过构造器创建的,那么很多依赖于构造器保证的约束条件在对象反序列化时都无法保证.比如一个设计成单例的类如果能够被序列化就可以分分钟克隆出多个实例...
Android 中的 Parcelable
相对于 Serializable 而言,Parcelable 的使用要复杂一些
从上述代码注释可以看出,写一个实现 Parcelable 接口的类还是比较麻烦的,和 Serailable 相比,我们需要在 writeToParcel 中按序写入各个域到流中,同样,在 createFromParcel 中我们需要自己返回一个 Book 对象.
public class Book implements Parcelable {
private String name;
public Book(String name) {
this.name = name;
}
protected Book(Parcel in) {
name = in.readString();
}
//反序列化功能由CREATOR完成,在CREATOR的内部标明的如何创建序列对象和数组
public static final Creator<Book> CREATOR = new Creator<Book>() {
//从Parcel中反序列化对象
@Override
public Book createFromParcel(Parcel in) {
//其内部调用Parcel的一系列readXXX方法实现反序列化过程
return new Book(in);
}
//创建序列化数组
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
//序列化过程:
//重写writeToParcel方法,我们要在这里逐一对需要序列化的属性用Parcel的一系列writeXXX方法写入
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
}
}
Parcelable 在使用上也与 Serializable 稍有不同
Parcelable 的写
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test2);
//获取一个Parcel容器
Parcel parcel = Parcel.obtain();
//需要序列化的对象
Book book = new Book("c++");
//把对象写入Parcel
parcel.writeParcelable(book,0);
//Parcel读写共用一个位置计数,这里一定要重置一下当前的位置
parcel.setDataPosition(0);
//读取Parcel
Book book1 = parcel.readParcelable(Book.class.getClassLoader());
Log.d("TestActivity",book1.toString());
}
}
我们来看一下 writeParcelable 方法
Parcelable 的读
[Parcel.java] public final void writeParcelable(Parcelable p, int parcelableFlags) {
//判断p是否为空
if (p == null) {
writeString(null);
return;
}
//① 先写入p的类名
writeParcelableCreator(p);
//② 调用我们重写的writeToParcel方法,按顺序写入域
p.writeToParcel(this, parcelableFlags);
}
public final void writeParcelableCreator(Parcelable p) {
//① 先写入p的类名
String name = p.getClass().getName();
writeString(name);
}
我们来看 readParcelable 方法
我们的测试例子读取 Parcel
[Parcel.java]
public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
//① 调用readParcelableCreator
//这时获得就是我们自定义的CREATOR
Parcelable.Creator<?> creator = readParcelableCreator(loader);
if (creator == null) {
return null;
}
// 判断当前creator是不是Parcelable.ClassLoaderCreator<?>的实例
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
//如果是的话,,我们调用reateFromParcel(this, loader);
Parcelable.ClassLoaderCreator<?> classLoaderCreator =
(Parcelable.ClassLoaderCreator<?>) creator;
return (T) classLoaderCreator.createFromParcel(this, loader);
}
//调用我们自定义的CREATOR中重写的createFromParcel方法
return (T) creator.createFromParcel(this);
}
public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
//首先把类名读取出来
String name = readString();
Parcelable.Creator<?> creator;
//mCreators做了一下缓存,如果之前某个classloader把一个parcelable的Creator获取过
//那么就不需要通过反射去查找了
synchronized (mCreators) {
HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader);
if (map == null) {
map = new HashMap<>();
mCreators.put(loader, map);
}
creator = map.get(name);
if (creator == null) {
try {
ClassLoader parcelableClassLoader =
(loader == null ? getClass().getClassLoader() : loader);
//加载我们自己实现Parcelable接口的类
Class<?> parcelableClass = Class.forName(name, false,
parcelableClassLoader);
Field f = parcelableClass.getField("CREATOR");
Class<?> creatorType = f.getType();
creator = (Parcelable.Creator<?>) f.get(null);
}
catch (Exception e) {
//catch exception
}
if (creator == null) {
throw new BadParcelableException("Parcelable protocol requires a "
+ "non-null Parcelable.Creator object called "
+ "CREATOR on class " + name);
}
map.put(name, creator);
}
}
return creator;
}
Book book1 = parcel.readParcelable(Book.class.getClassLoader());
可以看到我们在使用
readParcelable 的时候,传入的参数是 Book 类的类加载器,根据我们上面的代码,我们知道我们先会通过反射获取定义在 Book 类中的 CREATOR 属性,我们回想一下在 Book 类中是怎么定义 CREATOR 的
我们得到 CREATOR 属性后,调用它的 createFromParcel 方法,由多态可知调用的实际我们定义在 CREATOR 内的 createFromParcel 方法,在该方法内我们创建了 Book 对象(内部实现是通过 Parcel 的一系列 readXXX 方法)并返回.至此我们就得到了反序列化的对象
public static final Creator<Book> CREATOR = new Creator<Book>() {
//从Parcel中反序列化对象
@Override
public Book createFromParcel(Parcel in) {
//其内部调用Parcel的一系列readXXX方法实现反序列化过程
return new Book(in);
}
//创建序列化数组
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
本篇总结
我们本篇详细分析了 Android 序列化相关知识,你可以使用 Java 中的 Serializable 也可以使用 Parcelable.
下篇预告
下一篇文章是对前面所讲文章做一个小结.读者敬请期待哦.
此致,敬礼
11 小时前发布
来源: https://segmentfault.com/a/1190000012777029